Blog Post

Apps on Azure Blog
3 MIN READ

Collaborative Function App Development Using Repo Branches

theringe's avatar
theringe
Icon for Microsoft rankMicrosoft
Jul 10, 2025

In 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:

  1. Create a Function App

You can create a Function App using any method of your choice.

 

  1. Prepare a corresponding repo in Azure DevOps

Set up your repo structure for the Function App source code.

 

  1. 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: '<YOUR_CONNECTION_STRING_FROM_ADO>'
  functionAppName: '<YOUR_FUNCTION_APP_NAME>'
  resourceGroup: '<YOUR_RG_NAME>'
  location: '<YOUR_LOCATION_NAME>'
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 <YOUR_XXX> 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'
  }
}

 

 

  1. Deploy from the master branch

 

Once deployed, the HTTP trigger becomes active in the production slot, and can be accessed via:

https://<FUNCTION_APP_NAME>.azurewebsites.net/api/<TRIGGER_NAME>

 

  1. 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://<FUNCTION_APP_NAME>-<BRANCH_NAME>.azurewebsites.net/api/<TRIGGER_NAME>

 

Notice:

  1. 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.
  2. 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

 

Published Jul 10, 2025
Version 1.0
No CommentsBe the first to comment