Skip to content

Instantly share code, notes, and snippets.

@rajan-chari
Last active September 11, 2025 20:12
Show Gist options
  • Save rajan-chari/8b1c4d8c54609a8c89c42bf7cfef3d02 to your computer and use it in GitHub Desktop.
Save rajan-chari/8b1c4d8c54609a8c89c42bf7cfef3d02 to your computer and use it in GitHub Desktop.
Invoke CreateTunnel and Provision from powershell command line
#!/usr/bin/env pwsh
# teams-dev.ps1 - CLI wrapper for teams-dev-functions.ps1
# Usage: teams-dev.ps1 <subcommand> [options]
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
# 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
atk 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 (only when loaded as a module)
if ($null -ne $PSModuleInfo) {
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
$VERSION = '0.1.0'
function Show-GeneralHelp {
param()
Write-Host "teams-dev.ps1 v$VERSION" -ForegroundColor Cyan
Write-Host ""
Write-Host "Usage:" -ForegroundColor Yellow
Write-Host " teams-dev.ps1 <command> [options]"
Write-Host ""
Write-Host "Commands:" -ForegroundColor Yellow
Write-Host " create-tunnel (crt) Create a devtunnel" -ForegroundColor White
Write-Host " provision (prov) Provision TTK project for local development" -ForegroundColor White
Write-Host " help (h) Show help" -ForegroundColor White
Write-Host " version (v) Show version" -ForegroundColor White
Write-Host ""
Write-Host "Examples:" -ForegroundColor Yellow
Write-Host ' teams-dev.ps1 create-tunnel --name my-app --port 3978' -ForegroundColor White
Write-Host ' teams-dev.ps1 crt -n my-app -p 3978' -ForegroundColor White
Write-Host ' teams-dev.ps1 provision' -ForegroundColor White
}
function Show-SubHelp-CreateTunnel {
Write-Host "create-tunnel / crt" -ForegroundColor Cyan
Write-Host ""
Write-Host "Usage:" -ForegroundColor Yellow
Write-Host " teams-dev.ps1 create-tunnel --name <name> --port <port>" -ForegroundColor White
Write-Host ""
Write-Host "Options:" -ForegroundColor Yellow
Write-Host " -n, --name Tunnel name (required)" -ForegroundColor White
Write-Host " -p, --port Port number (required)" -ForegroundColor White
Write-Host " -h, --help Show this help" -ForegroundColor White
}
function Show-SubHelp-Provision {
Write-Host "provision / prov" -ForegroundColor Cyan
Write-Host ""
Write-Host "Usage:" -ForegroundColor Yellow
Write-Host " teams-dev.ps1 provision" -ForegroundColor White
Write-Host ""
Write-Host "Options:" -ForegroundColor Yellow
Write-Host " -h, --help Show this help" -ForegroundColor White
}
# helper to parse args for create-tunnel
function Parse-CreateTunnelArgs {
param(
[Parameter(ValueFromRemainingArguments=$true)]
[string[]]$Args
)
$result = @{ Name = $null; Port = $null; Help = $false }
for ($i=0; $i -lt $Args.Count; $i++) {
$a = $Args[$i]
switch ($a) {
'-n' {
if ($i+1 -lt $Args.Count) { $result.Name = $Args[$i+1]; $i++ } else { Write-Error "Missing value for $a"; exit 3 }
break
}
'--name' {
if ($i+1 -lt $Args.Count) { $result.Name = $Args[$i+1]; $i++ } else { Write-Error "Missing value for $a"; exit 3 }
break
}
'-p' {
if ($i+1 -lt $Args.Count) { $result.Port = $Args[$i+1]; $i++ } else { Write-Error "Missing value for $a"; exit 3 }
break
}
'--port' {
if ($i+1 -lt $Args.Count) { $result.Port = $Args[$i+1]; $i++ } else { Write-Error "Missing value for $a"; exit 3 }
break
}
'-h' {
$result.Help = $true; break
}
'--help' {
$result.Help = $true; break
}
default {
Write-Error "Unknown option: $a"; exit 3
}
}
}
return $result
}
# Main dispatch
if ($args.Count -eq 0) {
Show-GeneralHelp
exit 0
}
$cmd = $args[0].ToLower()
$subArgs = @()
if ($args.Count -gt 1) { $subArgs = $args[1..($args.Count-1)] }
switch ($cmd) {
'create-tunnel' { $cmdKey = 'create-tunnel' }
'crt' { $cmdKey = 'create-tunnel' }
'provision' { $cmdKey = 'provision' }
'prov' { $cmdKey = 'provision' }
'help' { $cmdKey = 'help' }
'h' { $cmdKey = 'help' }
'version' { $cmdKey = 'version' }
'v' { $cmdKey = 'version' }
default {
Write-Error "Unknown command: $cmd"
Show-GeneralHelp
exit 3
}
}
switch ($cmdKey) {
'create-tunnel' {
$parsed = Parse-CreateTunnelArgs -Args $subArgs
if ($parsed.Help) { Show-SubHelp-CreateTunnel; exit 0 }
if (-not $parsed.Name) { Write-Error "Tunnel name is required (-n, --name)"; exit 4 }
if (-not $parsed.Port) { Write-Error "Port is required (-p, --port)"; exit 4 }
if ($parsed.Port -notmatch '^\d+$') { Write-Error "Port must be an integer"; exit 4 }
try {
Create-Tunnel -TunnelName $parsed.Name -Port ([int]$parsed.Port)
exit $LASTEXITCODE
} catch {
Write-Error "Failed to create tunnel: $_"
exit 1
}
}
'provision' {
if ($subArgs -and ($subArgs -contains '-h' -or $subArgs -contains '--help')) { Show-SubHelp-Provision; exit 0 }
try {
Invoke-ProvisionTtkLocal
exit $LASTEXITCODE
} catch {
Write-Error "Provision failed: $_"
exit 1
}
}
'help' {
if ($subArgs.Count -gt 0) {
switch ($subArgs[0].ToLower()) {
'create-tunnel' { Show-SubHelp-CreateTunnel; exit 0 }
'crt' { Show-SubHelp-CreateTunnel; exit 0 }
'provision' { Show-SubHelp-Provision; exit 0 }
'prov' { Show-SubHelp-Provision; exit 0 }
default { Show-GeneralHelp; exit 0 }
}
} else {
Show-GeneralHelp; exit 0
}
}
'version' {
Write-Host $VERSION
exit 0
}
default {
Show-GeneralHelp; exit 0
}
}
@rajan-chari
Copy link
Author

PS C:> teams-dev
Teams Development Functions loaded successfully!
Available functions:

  • Create-Tunnel
  • Invoke-ProvisionTtkLocal
    teams-dev.ps1 v0.1.0

Usage:
teams-dev.ps1 [options]

Commands:
create-tunnel (crt) Create a devtunnel
provision (prov) Provision TTK project for local development
help (h) Show help
version (v) Show version

Examples:
teams-dev.ps1 create-tunnel --name my-app --port 3978
teams-dev.ps1 crt -n my-app -p 3978
teams-dev.ps1 provision

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