Skip to content

Instantly share code, notes, and snippets.

@heyitsaamir
Last active September 11, 2025 10:37
Show Gist options
  • Save heyitsaamir/d4ec5aee60c2d6c591e692b7dc0a4458 to your computer and use it in GitHub Desktop.
Save heyitsaamir/d4ec5aee60c2d6c591e692b7dc0a4458 to your computer and use it in GitHub Desktop.
Running Teams AI Library without ATK

Teams App Development Workflow (Without ATK Extension)

This guide explains how to run Teams apps using the Teams AI Library without relying on the ATK VS Code extension, using custom shell functions for a more controlled development experience.

Overview

Instead of using the ATK VS Code extension (which recreates devtunnels on every launch), this approach uses a long-living devtunnel and custom functions to streamline the development process.

Prerequisites

  • Dev Tunnels CLI installed
  • Teams Toolkit CLI (npm install -g @microsoft/teamsapp-cli)
  • Teams CLI (npm i -g @microsoft/teams.cli@preview)
  • Custom functions loaded in your shell:
    • Bash/Zsh: Source the teams-dev-functions.sh script (see Bash/Zsh setup below)
    • PowerShell: Load the teams-dev-functions.ps1 module (see PowerShell setup below)

Step-by-Step Process

1. Create a Long-Living Dev Tunnel

Use the tunnel creation function to create a persistent devtunnel:

Bash/Zsh:

create_tunnel <tunnel_name> <port>
# Example:
create_tunnel myteamsapp 3978

PowerShell:

Create-Tunnel -TunnelName "<tunnel_name>" -Port <port>
# Example:
Create-Tunnel -TunnelName "myteamsapp" -Port 3978

This function:

  • Creates a new devtunnel with the specified name
  • Sets up the port mapping
  • Configures anonymous access

2. Add ATK Configuration

Ensure your project has the correct ATK files:

npx @microsoft/teams.cli@preview config add atk.basic

3. Provision Your Bot

Use the provisioning function to register and provision your bot:

For TypeScript projects: Run from the root directory

Bash/Zsh:

provision_ttk_local

PowerShell:

Invoke-ProvisionTtkLocal

For .NET projects: Run from the TeamsApp directory

Bash/Zsh:

cd TeamsApp
provision_ttk_local

PowerShell:

Set-Location TeamsApp
Invoke-ProvisionTtkLocal

This function:

  • Creates env/.env.local if it doesn't exist
  • Prompts for and configures BOT_ENDPOINT and BOT_DOMAIN
  • Runs teamsapp provision --env=local

4. Run Your Application

You need two terminals running simultaneously:

Terminal 1: Host the Tunnel

devtunnel host <tunnel_name>

Example:

devtunnel host myteamsapp

Terminal 2: Run Your Server

For TypeScript/Node.js:

npm run dev

For .NET:

dotnet run --project <project-name>

Benefits of This Approach

  1. Persistent Tunnel: No need to update bot endpoints on every launch
  2. Better Control: Manual control over tunnel lifecycle
  3. Consistent URLs: Same tunnel URL across development sessions
  4. Cleaner Workflow: Separation of concerns between tunneling and application hosting

Shell Setup

Bash/Zsh Setup

To use the Bash/Zsh functions, source the script in your shell session or profile:

# Load the functions (run this once per session)
source ./teams-dev-functions.sh

# Or add to your shell profile (~/.bashrc, ~/.zshrc, etc.)
echo "source $(pwd)/teams-dev-functions.sh" >> ~/.zshrc

PowerShell Setup

To use the PowerShell functions, load the module in your PowerShell profile or session:

# Load the functions (run this once per session or add to your PowerShell profile)
. .\teams-dev-functions.ps1

# Or import as a module
Import-Module .\teams-dev-functions.ps1

To add to your PowerShell profile permanently:

# Add to your PowerShell profile
Add-Content $PROFILE ". `"$PWD\teams-dev-functions.ps1`""

Custom Functions Reference

Tunnel Creation

Bash/Zsh: create_tunnel(tunnel_name, port)
PowerShell: Create-Tunnel -TunnelName <name> -Port <port>

Creates a new devtunnel with the specified name and port, configured for anonymous access.

Bot Provisioning

Bash/Zsh: provision_ttk_local()
PowerShell: Invoke-ProvisionTtkLocal

Provisions a Teams Toolkit project for local development, handling environment configuration and bot registration.

Troubleshooting

  • Ensure your devtunnel is running before starting your application server
  • Verify the port in your tunnel matches the port your application is running on
  • Check that BOT_ENDPOINT in env/.env.local matches your tunnel URL
  • If you encounter issues with bot registration, verify your tunnel is accessible and anonymous access is enabled
# Teams Development Functions for PowerShell
# PowerShell equivalents of the custom shell functions for Teams app development
function Create-Tunnel {
<#
.SYNOPSIS
Creates a new devtunnel with the specified name and port, configured for anonymous access.
.DESCRIPTION
This function creates a persistent devtunnel that can be reused across development sessions,
avoiding the need to recreate tunnels and update bot endpoints on every launch.
.PARAMETER TunnelName
The name of the tunnel to create
.PARAMETER Port
The port number to map in the tunnel
.EXAMPLE
Create-Tunnel -TunnelName "my-teams-app" -Port 3978
#>
param(
[Parameter(Mandatory = $true)]
[string]$TunnelName,
[Parameter(Mandatory = $true)]
[int]$Port
)
try {
Write-Host "Creating devtunnel: $TunnelName" -ForegroundColor Green
# Create the tunnel
devtunnel create $TunnelName
if ($LASTEXITCODE -ne 0) {
throw "Failed to create tunnel"
}
# Create the port mapping
Write-Host "Creating port mapping for port: $Port" -ForegroundColor Green
devtunnel port create $TunnelName -p $Port
if ($LASTEXITCODE -ne 0) {
throw "Failed to create port mapping"
}
# Set up anonymous access
Write-Host "Setting up anonymous access" -ForegroundColor Green
devtunnel access create $TunnelName -p $Port --anonymous
if ($LASTEXITCODE -ne 0) {
throw "Failed to set up anonymous access"
}
Write-Host "βœ… Tunnel '$TunnelName' created successfully with port $Port and anonymous access" -ForegroundColor Green
}
catch {
Write-Error "❌ Failed to create tunnel: $_"
return 1
}
}
function Invoke-ProvisionTtkLocal {
<#
.SYNOPSIS
Provisions a Teams Toolkit project for local development.
.DESCRIPTION
This function handles environment configuration and bot registration for Teams Toolkit projects.
It creates the necessary .env.local file, prompts for bot endpoint configuration, and runs
the provision commands.
.EXAMPLE
Invoke-ProvisionTtkLocal
#>
try {
# Check if env folder exists
if (-not (Test-Path "env")) {
Write-Error "❌ 'env' folder not found. Are you sure this is a TTK enabled project?"
return 1
}
# Create env/.env.local if it doesn't exist
if (-not (Test-Path "env/.env.local")) {
New-Item -Path "env/.env.local" -ItemType File | Out-Null
Write-Host "✨ Created env/.env.local" -ForegroundColor Green
}
# Check and update BOT_ENDPOINT if needed
$envContent = Get-Content "env/.env.local" -ErrorAction SilentlyContinue
$botEndpointExists = $envContent | Where-Object { $_ -match "^BOT_ENDPOINT=" }
if (-not $botEndpointExists) {
Write-Host "BOT_ENDPOINT not found in env/.env.local" -ForegroundColor Yellow
$botEndpoint = Read-Host "Please enter the BOT_ENDPOINT value (e.g., https://example.com)"
if ([string]::IsNullOrEmpty($botEndpoint)) {
Write-Error "❌ BOT_ENDPOINT is required"
return 1
}
# Validate URL format
if ($botEndpoint -notmatch "^https?://") {
Write-Error "❌ BOT_ENDPOINT must start with http:// or https://"
return 1
}
# Extract domain by removing protocol
$botDomain = $botEndpoint -replace "^https?://", ""
# Remove any existing BOT_ENDPOINT and BOT_DOMAIN entries
$filteredContent = $envContent | Where-Object {
$_ -notmatch "^BOT_ENDPOINT=" -and $_ -notmatch "^BOT_DOMAIN="
}
# Add the new values
$newContent = $filteredContent + @("BOT_ENDPOINT=$botEndpoint", "BOT_DOMAIN=$botDomain")
$newContent | Set-Content "env/.env.local"
Write-Host "βœ… Added BOT_ENDPOINT and BOT_DOMAIN to env/.env.local" -ForegroundColor Green
}
# Run teamsapp provision
Write-Host "πŸš€ Running teamsapp provision..." -ForegroundColor Blue
teamsapp provision --env=local
if ($LASTEXITCODE -ne 0) {
Write-Error "❌ Provisioning failed"
return 1
}
Write-Host "βœ… Provision completed successfully" -ForegroundColor Green
return 0
}
catch {
Write-Error "❌ An error occurred during provisioning: $_"
return 1
}
}
# Export functions for use in other scripts
Export-ModuleMember -Function Create-Tunnel, Invoke-ProvisionTtkLocal
Write-Host "Teams Development Functions loaded successfully!" -ForegroundColor Green
Write-Host "Available functions:" -ForegroundColor Cyan
Write-Host " - Create-Tunnel" -ForegroundColor White
Write-Host " - Invoke-ProvisionTtkLocal" -ForegroundColor White
#!/bin/bash
# Teams Development Functions for Bash/Zsh
# Bash/Zsh functions for Teams app development workflow
create_tunnel() {
# Creates a new devtunnel with the specified name and port, configured for anonymous access
# Usage: create_tunnel <tunnel_name> <port>
# Example: create_tunnel my-teams-app 3978
if [ -z "$1" ] || [ -z "$2" ]; then
echo "❌ Usage: create_tunnel <tunnel_name> <port>"
echo " Example: create_tunnel my-teams-app 3978"
return 1
fi
local mytunnel=$1
local port=$2
echo "πŸš€ Creating devtunnel: $mytunnel"
# Create the tunnel
if ! devtunnel create "$mytunnel"; then
echo "❌ Failed to create tunnel"
return 1
fi
# Create the port mapping
echo "πŸ”— Creating port mapping for port: $port"
if ! devtunnel port create "$mytunnel" -p "$port"; then
echo "❌ Failed to create port mapping"
return 1
fi
# Set up anonymous access
echo "πŸ”“ Setting up anonymous access"
if ! devtunnel access create "$mytunnel" -p "$port" --anonymous; then
echo "❌ Failed to set up anonymous access"
return 1
fi
echo "βœ… Tunnel '$mytunnel' created successfully with port $port and anonymous access"
}
provision_ttk_local() {
# Provisions a Teams Toolkit project for local development
# Usage: provision_ttk_local
# Run from project root (TypeScript) or TeamsApp directory (.NET)
# Check if env folder exists
if [ ! -d "env" ]; then
echo "❌ 'env' folder not found. Are you sure this is a TTK enabled project?"
return 1
fi
# Create env/.env.local if it doesn't exist
if [ ! -f "env/.env.local" ]; then
touch "env/.env.local"
echo "✨ Created env/.env.local"
fi
# Check and update BOT_ENDPOINT if needed
if ! grep -q "^BOT_ENDPOINT=" "env/.env.local"; then
echo "BOT_ENDPOINT not found in env/.env.local"
read "bot_endpoint?Please enter the BOT_ENDPOINT value (e.g., https://example.com): "
if [ -z "$bot_endpoint" ]; then
echo "❌ BOT_ENDPOINT is required"
return 1
fi
# Validate URL format
if ! echo "$bot_endpoint" | grep -qE '^https?://'; then
echo "❌ BOT_ENDPOINT must start with http:// or https://"
return 1
fi
# Extract domain by removing protocol
bot_domain=$(echo "$bot_endpoint" | sed -E 's#^https?://##')
# Remove any existing BOT_ENDPOINT and BOT_DOMAIN entries
sed -i.bak '/^BOT_ENDPOINT=/d; /^BOT_DOMAIN=/d' "env/.env.local"
rm -f "env/.env.local.bak"
# Append both values to .env.local
echo "BOT_ENDPOINT=$bot_endpoint" >> env/.env.local
echo "BOT_DOMAIN=$bot_domain" >> env/.env.local
echo "βœ… Added BOT_ENDPOINT and BOT_DOMAIN to env/.env.local"
fi
echo "πŸš€ Running teamsapp provision..."
if ! teamsapp provision --env=local; then
echo "❌ Provisioning failed"
return 1
fi
echo "βœ… Provision completed successfully"
return 0
}
# Function to display help
show_teams_dev_help() {
echo "Teams Development Functions"
echo "=========================="
echo ""
echo "Available functions:"
echo " create_tunnel <tunnel_name> <port> - Create a persistent devtunnel"
echo " provision_ttk_local - Provision Teams Toolkit project for local dev"
echo " show_teams_dev_help - Show this help message"
echo ""
echo "Usage Examples:"
echo " create_tunnel my-teams-app 3978"
echo " provision_ttk_local"
}
echo "βœ… Teams Development Functions loaded successfully!"
echo "Available functions: create_tunnel, provision_ttk_local"
echo "Type 'show_teams_dev_help' for usage information"
@rajan-chari
Copy link

rajan-chari commented Sep 11, 2025

Thanks Aamir!
CLI version of powershell script. Can be used from command line: https://gist.github.com/rajan-chari/8b1c4d8c54609a8c89c42bf7cfef3d02

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment