Table of Contents
Azure DevOps Pipeline with Azure App Configuration
Summary: On this page I'll show you how to use the Azure App Configuration in an Azure DevOps Pipeline.
Date: 5 January 2025
Note: See here for the bicep module to deploy an Azure App Configuration Store.
What is Azure App Configuration
Azure App Configuration is a service that allows you to centralize your application settings and feature flags. It provides a way to manage the settings of your application in a single place, and it can be used to store key-value pairs that can be accessed by your application at runtime. I found it very useful as it also allows you to import variables into your Azure DevOps pipeline, allowing you to also manage your pipeline variables in a single place.
Azure App Configuration Tasks
Before you can use the Azure App Configuration tasks in your Azure DevOps pipeline you need to install the Azure App Configuration extension:
In case you haven't done so, you also need to setup an Azure DevOps Service Connection to your Azure Subscription. You can check the documentation here (import) and here (export) on how to do that.
Azure DevOps Pipeline
Download Variables
The pipeline below downloads all variables from the Azure App Configuration Store. For more info on the pipeline see Cheatsheet Azure DevOps. It downloads all variables with a global or dev label. In the app configuration store that is used for this task, all variables either have a global label or an environment label (dev, tst, acc, prd). This makes sure the correct variables are downloaded for the correct environment.
The last task is optional as it only logs the variables. This is useful for debugging the pipeline.
name: $(Build.DefinitionName)-$(Build.BuildId) variables: buildConfiguration: 'Release' deploymentSuffix: '$(Build.DefinitionName)-$(Build.BuildId).$(System.StageAttempt).$(System.JobAttempt)' parameters: - name: alllogs displayName: "Enable all logging" type: boolean default: true pool: vmImage: ubuntu-latest trigger: none stages: - stage: build displayName: "Stage: Build" pool: name: BuildPoolDotNet jobs: - job: build displayName: "Job: Build & Test" steps: - checkout: self clean: true - task: AzureAppConfiguration@7 displayName: "Pull App Configuration Variables" inputs: azureSubscription: 'arm-shift' AppConfigurationEndpoint: 'https://appcs-euw-shift.azconfig.io' KeyFilter: '*' Label: 'global,dev' SuppressWarningForOverriddenKeys: true retryCountOnTaskFailure: 3 - task: PowerShell@2 displayName: "Log: Variables" inputs: pwsh: true targetType: 'inline' script: | Write-Host "`n##[section]Show pipeline variables`n" Get-ChildItem -path env:* | Sort Name
Note that you could replace the label with a parameter from the pipeline:
Label: 'global,${{ parameters.environment }}'
Push Variables
Pushing variables to the Azure App Configuration Store is a bit more complex. The pipeline below first creates a file with the variables that need to be pushed, in yaml format. If no variables are found that need to be pushed it sets a pipeline variable 'AppConfig' and sets it to false. The next task checks for this variable so it doesn't fail because there is no file with variables, and then it pushes the variables to the App Configuration Store.
- task: PowerShell@2 displayName: "App Config: Prepare" inputs: pwsh: true targetType: 'inline' script: | Write-Host "##[section]Create push file" $pushConfigFile = "$env:Build_SourcesDirectory\$(Build.Repository.Name)\push\appconfigupload.yml" Write-Host "Push file dir: $pushConfigFile " if (Test-Path -Path $pushConfigFile -PathType leaf){ Write-Host "Push file already exists. Remove $pushConfigFile" Remove-Item -Path $pushConfigFile -Force } $services = Get-ChildItem -path env:* | Where-Object {$_.name -like '*SERVIVE'} | select name,value if ($services){ # create empty file and needed directories New-Item -Path $pushConfigFile -ItemType File -Force # add the variables to yml file foreach ($ervice in $services){ $name = $service.Name $value = $service.Value Write-Host "Adding service: $name; value: $value" Out-File -FilePath $pushConfigFile -InputObject "$name`: $value" -Append } }else{ Write-Host "No service variables have been found to upload to App Configuration" Write-Host "##vso[task.setvariable variable=AppConfig]False" } - task: AzureAppConfigurationPush@6 displayName: "App Config: Push variables" condition: and(succeeded(), not(eq(variables['AppConfig'], False))) inputs: azureSubscription: 'arm-shift' AppConfigurationEndpoint: 'https://appcs-euw-shift.azconfig.io' ConfigurationFile: '$(BUILD.SOURCESDIRECTORY)/$(BUILD.REPOSITORY.NAME)/push/appconfigupload.yml' Separator: ':' Label: ${{ parameters.environment }} Strict: false DryRun: false ImportMode: 'Ignore-Match' retryCountOnTaskFailure: 3
Backup Variables
Even though you can create replicas of the Azure App Configuration Store, it's always a good idea to have a backup of your variables. This allows you to check if a variable was present in the app configuration store at a certain point in time and what the value was of that variable. The first taks below creates a backup file for each label in the app configuration store. The second task publishes the backup files as a pipeline artifact. This means the backup files will be lost after a while, so you could think about using a separate script to change this.
- task: AzureCLI@2 displayName: Backup App Configuration condition: succeededOrFailed() env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) inputs: azureSubscription: 'arm-shift' scriptType: pscore scriptLocation: inlineScript inlineScript: | # Set variables for app configuration in production $subId = "aa123456-a123-a123-a123-abcd12345678" $appcsName = "appcs-euw-shift" $backupDir = "$env:BUILD_ARTIFACTSTAGINGDIRECTORY/exported-appcs-configs" # Create backup directory if ( -not (Test-Path $backupDir) ){ New-Item $backupDir -Type directory -Force } az account set --subscription $subId Write-Host '##[Section]Create backup files per label' [array]$allLabels = az appconfig kv list --name $appcsName --auth-mode login | ConvertFrom-Json | Select-Object label -Unique foreach ($label in $allLabels) { $labelName = $label.label Write-Host "Creating backup for label: $labelName" az appconfig kv export --name $appcsName --auth-mode login --destination file --path "$backupDir/$labelName.yaml" --format yaml --label $labelName --yes } - task: PublishPipelineArtifact@1 displayName: 'Publish Backup App Configuration' inputs: targetPath: '$(Build.ArtifactStagingDirectory)/exported-appcs-configs' artifactName: backupAppConfig artifactType: pipeline parallel: true