= 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 \\
{{tag>azure devops}}
> Note: See [[bicepmoduleappconfiguration|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:
* [[https://marketplace.visualstudio.com/items?itemName=AzureAppConfiguration.azure-app-configuration-task-push|Azure App Configuration Push]]
* [[https://marketplace.visualstudio.com/items?itemName=AzureAppConfiguration.azure-app-configuration-task|Azure App Configuration]]
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 [[https://learn.microsoft.com/en-us/azure/azure-app-configuration/azure-pipeline-import-task|here (import)]] and [[https://learn.microsoft.com/en-us/azure/azure-app-configuration/azure-pipeline-export-task|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-azuredevops]]. 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 [[scriptazuredevopsapikeepforever|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