Curious About Model Context Protocol? Dive into MCP with Us!
July 10, 2025Announcing the new Microsoft Learn Plan – Preparing for your organization’s AI workloads
July 10, 2025In this example, I demonstrate a Windows-based Function App using PowerShell, with deployment via Azure DevOps (ADO) and a Bicep template. Local development is done in VSCode.
Scenario:
Your Function App project resides in a shared repository maintained by a team. Each developer works on a separate branch. Whenever a branch is updated, the Function App is deployed to a slot named after that branch. If the slot doesn’t exist, it will be automatically created.
How to use it:
Create a Function App
You can create a Function App using any method of your choice.
Prepare a corresponding repo in Azure DevOps
Set up your repo structure for the Function App source code.
Create Function App code using the VSCode wizard
In this example, we use PowerShell and create an anonymous HTTP trigger. Then, we manually add three additional files. The resulting directory structure looks like this:
deploy.yml
trigger:
branches:
include:
– ‘*’
pool:
vmImage: ‘ubuntu-latest’
variables:
azureSubscription: ”
functionAppName: ”
resourceGroup: ”
location: ”
steps:
– checkout: self
– task: AzureCLI@2
name: DeploySlotInfra
inputs:
azureSubscription: $(azureSubscription)
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
BRANCH_NAME=$(Build.SourceBranchName)
if [ “$BRANCH_NAME” = “master” ]; then
echo “##[command]Deploying production infrastructure”
az deployment group create
–resource-group $(resourceGroup)
–template-file deploy-master.bicep
–parameters functionAppName=$(functionAppName) location=$(location)
else
SLOT_NAME=”$BRANCH_NAME”
echo “##[command]Deploying slot: $SLOT_NAME”
az deployment group create
–resource-group $(resourceGroup)
–template-file deploy.bicep
–parameters functionAppName=$(functionAppName) slotName=$SLOT_NAME location=$(location)
fi
– task: ArchiveFiles@2
displayName: ‘Package Function App as ZIP’
inputs:
rootFolderOrFile: ‘$(System.DefaultWorkingDirectory)/’
includeRootFolder: false
archiveType: zip
archiveFile: ‘$(Build.ArtifactStagingDirectory)/functionapp.zip’
replaceExistingArchive: true
– task: AzureCLI@2
name: ZipDeploy
inputs:
azureSubscription: $(azureSubscription)
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
BRANCH_NAME=$(Build.SourceBranchName)
if [ “$BRANCH_NAME” = “master” ]; then
echo “##[command]Deploying code to production”
az functionapp deployment source config-zip
–name $(functionAppName)
–resource-group $(resourceGroup)
–src “$(Build.ArtifactStagingDirectory)/functionapp.zip”
else
SLOT_NAME=”$BRANCH_NAME”
echo “##[command]Deploying code to slot: $SLOT_NAME”
az functionapp deployment source config-zip
–name $(functionAppName)
–resource-group $(resourceGroup)
–slot $SLOT_NAME
–src “$(Build.ArtifactStagingDirectory)/functionapp.zip”
fi
Please replace all placeholders with values relevant to your environment.
Additionally, update the two instances of “master” to match your repo’s default branch name (e.g., main), as updates from this branch will always deploy to the production slot.
deploy-master.bicep
@description(‘Function App Name’)
param functionAppName string
@description(‘Function App location’)
param location string
resource functionApp ‘Microsoft.Web/sites@2022-09-01’ existing = {
name: functionAppName
}
resource appSettings ‘Microsoft.Web/sites/config@2022-09-01’ = {
name: ‘appsettings’
parent: functionApp
properties: {
FUNCTIONS_EXTENSION_VERSION: ‘~4’
}
}
deploy.bicep
@description(‘Function App Name’)
param functionAppName string
@description(‘Slot Name (e.g., dev, test, feature-xxx)’)
param slotName string
@description(‘Function App location’)
param location string
resource functionApp ‘Microsoft.Web/sites@2022-09-01’ existing = {
name: functionAppName
}
resource functionSlot ‘Microsoft.Web/sites/slots@2022-09-01’ = {
name: slotName
parent: functionApp
location: location
properties: {
serverFarmId: functionApp.properties.serverFarmId
}
}
resource slotAppSettings ‘Microsoft.Web/sites/slots/config@2022-09-01’ = {
name: ‘appsettings’
parent: functionSlot
properties: {
FUNCTIONS_EXTENSION_VERSION: ‘~4’
}
}
Deploy from the master branch
Once deployed, the HTTP trigger becomes active in the production slot, and can be accessed via:
https://.azurewebsites.net/api/
Switch to a custom branch like member1 and create a test HTTP trigger
After publishing, a new deployment slot named member1 will be created (if not already existing).
You can open it in the Azure Portal and view its dedicated interface.
The branch-specific HTTP trigger will now work at the following URL:
https://-.azurewebsites.net/api/
Notice:
- Using deployment slots for collaborative development is subject to slot count and SKU limits. For example, the Premium SKU supports up to 20 slots. See the Azure subscription and service limits, quotas, and constraints – Azure Resource Manager | Microsoft Learn for details.
- If you need to delete a slot after use, you can do so using PowerShell with the Remove-AzWebAppSlot command: Remove-AzWebAppSlot (Az.Websites) | Microsoft Learn