Skip to content

Instantly share code, notes, and snippets.

@heyitsaamir
Last active September 26, 2025 22:32
Show Gist options
  • Save heyitsaamir/232faffd0e5d84a28f4062f9c4188a6b to your computer and use it in GitHub Desktop.
Save heyitsaamir/232faffd0e5d84a28f4062f9c4188a6b to your computer and use it in GitHub Desktop.
Agent Provisioning For Teams

Agent Provisioning for Teams

Creation

Note

This guide mainly covers Provisioning, not creation of the agent. But here is a simplified version of that...

If you use the sdk's CLI, you can have an app set up in just a few commands. Here's the command for python for example:

npx @microsoft/teams.cli new python my-super-agent -t echo

This creates an echo agent in python using our CLI. More details are in https://microsoft.github.io/teams-ai/python/getting-started/quickstart. Use whatever language you like.


The process of having your agent run on Teams involves a few major steps:

  1. Have your app running and accepting requests at some endpoint. Using the SDK simplifies this process for you.
  2. Provision your agent (i.e. bot registration)
  3. Install your app to Microsoft Teams

Provisioning

Note

In the sections below, I use the Azure CLI. You need to make sure you have this cli installed and you're logged into Azure CLI using az login. Typically this means you need to select a subscription as well.

Tip

Details of the commands etc are collapsed in each bullet point. You can expand it to take a deeper look.

An agent that is allowed to run on Teams must be provisioned. Provisioning basically means this:

1. An Application needs to be registered with Microsoft Entra. This application needs to have a unique _global_ name. You get an application id when you register your application with Entra.
  appId=$(az ad app create --display-name $botName --sign-in-audience "AzureADMyOrg" --query appId -o tsv)
  echo "App ID: $appId"
  az ad sp create --id $appId
  appCred=$(az ad app credential reset --id $appId)
  tenantId=$(echo $appCred | jq -r '.tenant')
  clientSecret=$(echo $appCred | jq -r '.password')
  echo "Tenant ID: $tenantId"
  echo "Client Secret: $clientSecret"

What just happened:

  1. We created an Azure AD application with a display name and got back an App ID
  2. We created a service principal for that application
  3. We reset the application credentials to get a fresh client secret
2. Next, you need to create an Azure Bot. This is a particular type of resource which you create in a particular tenant. The Azure Bot points to the application id for identification.
echo "Resource Group: $(az group create --name $resourceGroup --location $location --query name -o tsv)"
echo "Bot Resource ID: $(az bot create --resource-group $resourceGroup --name $botName --app-type SingleTenant --appid $appId --endpoint $endpointUrl --sku F0 --query id -o tsv)"

What just happened:

  1. We created a resource group to contain our Azure resources
  2. We created an Azure Bot resource in that resource group
  3. We linked it to our Azure AD application using the App ID
  4. We configured the bot endpoint where Teams will send messages
  5. We used the F0 (free) pricing tier
3. Finally, you need to enable the Azure Bot to work in Microsoft Teams.
echo "Teams Channel: $(az bot msteams create --name $botName --resource-group $resourceGroup --query name -o tsv)"

What just happened:

  1. We enabled the Microsoft Teams channel for our Azure Bot
  2. This allows the bot to receive messages from Teams users
4. Save the credentials to a .env file for your application.
botDomain=$(echo "$endpointUrl" | sed -E 's#^https?://##' | sed 's#/.*##')
echo "TENANT_ID=$tenantId" > .env
echo "CLIENT_ID=$appId" >> .env
echo "CLIENT_SECRET=$clientSecret" >> .env
echo "BOT_DOMAIN=$botDomain" >> .env
echo "Environment variables saved to .env"

What just happened:

  1. We extracted the domain from the bot endpoint URL
  2. We created a .env file with all the credentials your application needs
  3. The file contains TENANT_ID, CLIENT_ID, CLIENT_SECRET, and BOT_DOMAIN

With the above, your agent is provisioned successfully and is able to receive messages from Teams.

Tip

There is a script you can run that will automate this for you at the bottom of this gist Run it like this: ./provision_bot.sh. It'll ask you a few questions and run the above commands for you and output a .env file.

App Package

Next, we need to tell Microsoft Teams about your Agent. For this, we use an app package, which essentially contains:

  1. A manifest - This manifest contains all the details for your agent including its application id.
  2. Icons

So once you provision, you build this app package (which results in a zip file asset). You need to open the manifest.json file that was automatically created for you, and replace all the values with the values from the provisioning step.

Tip

There is a script you can run that will automate this for you at the bottom of this gist Run it like ./build_apppackage.sh ./apppackage .env. Make sure your .env file is up-to-date. It'll produce a zip file that you can sideload into Teams.

Sideloading it to Teams

Once you have provisioned your agent AND you have built the app package, you need to install it into Teams. Follow these steps to do that - https://learn.microsoft.com/en-us/microsoftteams/platform/concepts/deploy-and-publish/apps-upload#prerequisites

#!/bin/bash
set -e
# Function to show usage
show_usage() {
echo "Usage: $0 <apppackage_folder> <env_file>"
echo ""
echo "Arguments:"
echo " apppackage_folder Path to folder containing manifest.json, color.png, and outline.png"
echo " env_file Path to .env file for variable substitution"
echo ""
echo "Example:"
echo " $0 /path/to/apppackage .env"
exit 1
}
# Check if correct number of arguments provided
if [ $# -ne 2 ]; then
echo "Error: Wrong number of arguments"
show_usage
fi
apppackage_folder="$1"
env_file="$2"
# Convert paths to absolute paths
apppackage_folder=$(realpath "$apppackage_folder")
env_file=$(realpath "$env_file")
# Validate apppackage folder exists
if [ ! -d "$apppackage_folder" ]; then
echo "Error: Apppackage folder '$apppackage_folder' does not exist"
exit 1
fi
# Validate env file exists
if [ ! -f "$env_file" ]; then
echo "Error: Environment file '$env_file' does not exist"
exit 1
fi
echo "Building apppackage from: $apppackage_folder"
echo "Using environment file: $env_file"
# Change to apppackage directory
cd "$apppackage_folder"
# Validate required files exist
required_files=("manifest.json" "color.png" "outline.png")
for file in "${required_files[@]}"; do
if [ ! -f "$file" ]; then
echo "Error: Required file '$file' not found in $apppackage_folder"
exit 1
fi
done
echo "✓ All required files found (manifest.json, color.png, outline.png)"
# Create dist folder if it doesn't exist
mkdir -p dist
echo "Processing manifest.json with environment variables..."
# Export variables from env file and process manifest
set -a # automatically export all variables
source "$env_file"
set +a # stop automatically exporting
# Handle variable mapping and defaults for Teams app manifest
echo "Setting up Teams app variable mappings..."
# Map CLIENT_ID to various Teams app ID variables
if [ -n "$CLIENT_ID" ]; then
export TEAMS_APP_ID="$CLIENT_ID"
export BOT_ID="$CLIENT_ID"
echo "✓ Mapped CLIENT_ID ($CLIENT_ID) to TEAMS_APP_ID and BOT_ID"
fi
# Set APP_NAME_SUFFIX to "local" if it exists
if [ -n "$APP_NAME_SUFFIX" ] || grep -q "APP_NAME_SUFFIX" "$env_file"; then
export APP_NAME_SUFFIX="local"
echo "✓ Set APP_NAME_SUFFIX to 'local'"
fi
# Extract BOT_DOMAIN from BOT_ENDPOINT if BOT_DOMAIN is not set
if [ -z "$BOT_DOMAIN" ] && [ -n "$BOT_ENDPOINT" ]; then
export BOT_DOMAIN=$(echo "$BOT_ENDPOINT" | sed -E 's#^https?://##' | sed 's#/.*##')
echo "✓ Extracted BOT_DOMAIN ($BOT_DOMAIN) from BOT_ENDPOINT"
elif [ -n "$BOT_DOMAIN" ]; then
echo "✓ Using existing BOT_DOMAIN ($BOT_DOMAIN)"
fi
# Replace specific variables in ${{...}} format
sed \
-e "s/\${{TEAMS_APP_ID}}/$TEAMS_APP_ID/g" \
-e "s/\${{APP_NAME_SUFFIX}}/$APP_NAME_SUFFIX/g" \
-e "s/\${{BOT_ID}}/$BOT_ID/g" \
-e "s/\${{BOT_DOMAIN}}/$BOT_DOMAIN/g" \
manifest.json > dist/manifest.json
echo "✓ Processed manifest saved to dist/manifest.json"
# Copy image files to dist folder
cp color.png dist/
cp outline.png dist/
echo "✓ Copied image files to dist folder"
# Create the zip file
echo "Creating apppackage.zip..."
cd dist
zip -r ../apppackage.zip manifest.json color.png outline.png
cd ..
echo "✅ Successfully created apppackage.zip"
echo "Files included:"
unzip -l apppackage.zip
# Clean up dist folder
rm -rf dist
echo "✓ Cleaned up temporary dist folder"
#!/bin/bash
set -e
# Default output location
output_location="."
# Parse command line arguments
while [[ $# -gt 0 ]]; do
case $1 in
-o|--output)
output_location="$2"
shift 2
;;
-h|--help)
echo "Usage: $0 [-o|--output <directory>]"
echo " -o, --output Output directory for environment files (default: current directory)"
exit 0
;;
*)
echo "Unknown option: $1"
echo "Usage: $0 [-o|--output <directory>]"
exit 1
;;
esac
done
# Ensure output directory exists
if [ ! -d "$output_location" ]; then
echo "Error: Output directory '$output_location' does not exist."
exit 1
fi
# Ensure Azure CLI is installed
if ! command -v az >/dev/null 2>&1; then
echo "Error: Azure CLI ('az') is not installed. Install it from https://aka.ms/azcli and try again."
exit 1
fi
# Ensure Azure CLI is logged in and has a subscription set
subscriptionId=$(az account show --query id -o tsv 2>/dev/null || true)
if [ -z "$subscriptionId" ]; then
echo "Error: Azure CLI is not logged in or no subscription is set."
echo "Run 'az login' to sign in, then 'az account set --subscription <id|name>' to set a subscription."
exit 1
fi
# Handle resource group creation or reuse
echo "Resource Group Management:"
read -r -p "Do you want to (c)reate a new resource group or (r)euse an existing one? [c/r]: " rg_choice
case $rg_choice in
c|C|create|CREATE)
read -r -p "Enter new resource group name: " resourceGroup
read -r -p "Enter Azure region (e.g., eastus, westus2): " region
if [[ -z "$resourceGroup" || -z "$region" ]]; then
echo "Error: Resource group name and region must be provided."
exit 1
fi
echo "Creating resource group '$resourceGroup' in region '$region'..."
az group create --name "$resourceGroup" --location "$region"
echo "Resource group '$resourceGroup' created successfully."
;;
r|R|reuse|REUSE)
read -r -p "Enter existing resource group name: " resourceGroup
if [[ -z "$resourceGroup" ]]; then
echo "Error: Resource group name must be provided."
exit 1
fi
# Verify the resource group exists
if ! az group show --name "$resourceGroup" >/dev/null 2>&1; then
echo "Error: Resource group '$resourceGroup' does not exist."
exit 1
fi
echo "Using existing resource group '$resourceGroup'."
;;
*)
echo "Error: Invalid choice. Please enter 'c' for create or 'r' for reuse."
exit 1
;;
esac
# Prompt for remaining variables
read -r -p "Enter Bot name: " botName
read -r -p "Enter endpoint URL (e.g. https://my.devtunnel.ms/api/messages): " endpointUrl
echo "Creating azure Bot Service with the following details:"
echo "Subscription ID: $subscriptionId"
echo "Resource Group: $resourceGroup"
echo "Bot Name: $botName"
echo "Endpoint URL: $endpointUrl"
echo " "
if [[ -z "$resourceGroup" || -z "$botName" || -z "$endpointUrl" ]]; then
echo "Error: All variables must be provided."
exit 1
fi
echo "Creating Entra ID app registration..."
appId=$(az ad app create --display-name $botName --sign-in-audience "AzureADMyOrg" --query appId -o tsv)
az ad sp create --id $appId
appCred=$(az ad app credential reset --id $appId)
tenantId=$(echo $appCred | jq -r '.tenant')
clientSecret=$(echo $appCred | jq -r '.password')
echo "App registration created with App ID: $appId"
echo "Creating Bot Service $botName in resource group $resourceGroup..."
az bot create \
--name $botName \
--app-type SingleTenant \
--appid $appId \
--tenant-id $tenantId \
--resource-group $resourceGroup \
--endpoint $endpointUrl
# Optional OAuth auth setting for Microsoft Graph
read -r -p "Do you want to create a graph auth OAuth setting? [y/N]: " create_auth
case $create_auth in
y|Y|yes|YES)
echo "Creating OAuth auth setting for Microsoft Graph..."
az bot authsetting create \
--resource-group $resourceGroup \
--name $botName \
-c "graph" \
--client-id $appId \
--client-secret $clientSecret \
--provider-scope-string "User.Read" \
--service "Aadv2" \
--parameters "clientId=$appId" "clientSecret=$clientSecret" "tenantId=$tenantId" "tokenExchangeUrl=api://$appId"
echo "OAuth auth setting created successfully."
;;
*)
echo "Skipping OAuth auth setting creation."
;;
esac
az bot msteams create \
--name $botName \
--resource-group $resourceGroup
botDomain=$(echo "$endpointUrl" | sed -E 's#^https?://##' | sed 's#/.*##')
echo "TENANT_ID=$tenantId" >"$output_location/.env"
echo "CLIENT_ID=$appId" >>"$output_location/.env"
echo "CLIENT_SECRET=$clientSecret" >>"$output_location/.env"
echo "BOT_DOMAIN=$botDomain" >>"$output_location/.env"
echo "Environment variables saved to $output_location/.env"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment