Artikel Azure App Configuration wat is het en hoe automatiseer je het met Azure DevOps

Inleiding

Door een collega werd ik geattendeerd op de Twelve Factor App (https://12factor.net/). De twelve-factor beschrijft een methodologie die kan worden toegepast op apps geschreven in ieder willekeurige programmeertaal en die één of meer 'backing services' (bijv. database, queue, memory cache, etc) gebruiken.

Een belangrijk punt in deze methodologie is de ontkoppeling van applicatie en configuratie. Dat klinkt mooi, maar hoe doe je dat dan? De schrijvers van de Twelve Factor App stellen voor om configuratie vooral in environment variabelen op te slaan omdat zij zien dat het gebruik van configuratie bestanden een aantal zwakheden hebben. Hierbij moet gedacht worden aan:

  • Configuratie bestanden hebben de neiging om zich al snel te verspreiden
  • Het vergt nogal discipline van de ontwikkelaars om het formaat over verschillende configuratiebestanden gelijk te houden
  • Het formaat van configuratie bestanden hebben de neiging om taal- of frameworkspecifiek te worden.
  • Ik weet niet hoe het in andere projecten gaat maar bij mijn projecten is het altijd een hoop gehannis met secrets, waar sla je die op, hoe sla je die op? Het laatste wat je wil is dat productie wachtwoorden plain text in configuratie bestanden terechtkomen.

Het belangrijkste is dat het moeilijk is overzicht te houden over alle configuratie wanneer een platform uit meerdere applicaties bestaat.

Azure App Configuration

Om ons te helpen weer iets beter aan de Twelve Factor App principes te voldoen heb ik me verdiept in het gebruik van de Azure App Configuration. Deze service biedt de mogelijkheid om configuratie items in een Key Value pair formulier (zie figuur 1) online op te voeren en evt. te voorzien van een label waarmee je op te voeren keys aan een stage van een release zou kunnen koppelen. Uitleg over het gebruik van deze labels is te vinden in de blog (Hoe consumeer je items uit een Azure App Configuration).

Naarmate een applicatie connectie maakt met meer achterliggende systemen als databases en api's heeft de configuratie ook de neiging om erg hard te groeien. Menig ontwikkelaar zal het het niet prettig vinden om die keys dan elke keer met de hand op te moeten voeren. Maar ook hier biedt de Azure App Configuration een oplossing door de mogelijkheid te bieden om reeds bestaande configuratie bestanden te importeren.

Nieuw configuratie item formulier

Figuur 1: Voorbeeld van het invoer formulier voor het opvoeren van een nieuwe configuratie.

Azure Devops

In een release pipeline kan je dankbaar gebruik maken van deze import functionaliteit door met behulp van de Azure CLI configuratie bestanden in de opgeleverde bestanden te gebruiken om een Azure App Configuration service te voeden. Een goede ontwikkelaar wordt gekenmerkt door een hekel te hebben aan het uitvoeren van repetitieve taken. Dergelijke taken lenen zich over het algemeen prima om te automatiseren, dus waarom het aanmaken, vullen en connecten van een Azure App Configuration dan niet?

In een release pipeline wordt gewerkt met desired state, dus ongeacht de situatie voordat een release naar een omgeving wordt uitgerold, zodra de release is uitgerold wil je dat alle geraakte applicaties in de gewenste state zijn zodat je zeker weet dat de opgeleverde applicaties ook daadwerkelijk gaan werken.

Je zou in je release pipeline een stap kunnen toevoegen die het volgende doet:

  1. Maak een Azure App Configuration aan
  2. Importeer configuratie keys die niet omgeving specifiek zijn (bijv.uit appsettings.json)
  3. Importeer omgeving specifieke keys (bijv. uit appsettings.<Environment>.json)
  4. Maak connectie met een Key Vault voor secrets
  5. Importeer secrets uit de Key Vault
  6. Lees de connection string naar de Azure App Configuration uit
  7. Ken de in stap 6 uitgelezen connectionstring toe aan de connectionstrings van de te installeren applicatie
  8. Herstart de applicatie.

Dit zijn veel stappen, maar het mooie is, je programmeert ze één keer uit en daarna hoef je er nooit meer naar om te kijken. En bij alle stappen geldt: Het is desired state , dus als de gewenste state al is bereikt, dan wordt die stap overgeslagen.

Wat moet je hiervoor doen?

  1. In een release pipeline (bestaand of nieuw) voeg een 'Azure CLI' stap toe.
  2. Voor het hieronder beschreven script is het wel nodig om de Task Version op 2.* te zetten.
  3. Kies de Azure subscription waar Azure App Configuration
  4. Kies als Script Type 'Powershell Core' (Hiervoor is het belangrijk dat je in stap 2 de task version aanpast!)
  5. Kies een inline script
  6. Maak het script waar hieronder een voorbeeld wordt gegeven. In onderstaande screenshot is maar een deel zichtbaar!

Azure CLI Release stap

Figuur 2: Voorbeeld van de instellingen van de Azure CLI taak.

Het script

Het aanmaken van de Azure App Configuration

az appconfig create -g "$(ResourceGroupName)" -n "$(AppConfiguration)" -l "$(AzureLocation)" --sku standard

Het importeren van de algemene configuratie items

az appconfig kv import --name "$(AppConfiguration)" --label Default --source file --path $(ConfigurationDirectory)/appsettings.json --format json --separator : --prefix "$(AppConfigurationPrefix)" --yes

Beschrijving parameters: --name : Naam van de Azure App Configuration waar naartoe te importeren --label : Het label toe te kennen aan de te importeren items --source : Het type bron, in dit geval 'file' omdat we een appsettings.json bestand gaan importeren --path : Het pad naar het bron bestand. --format : Het format waarin de configuratie items in de bron zijn opgeslagen, in dit geval is dat json --separator : Het scheidingsteken tussen de verschillende stappen in het bron bestand. Het betreft hier JSON met een bepaalde object structuur, het hele pad van 'root' naar een property in de json wordt in de key opgenomen en wordt gescheiden door het hier opgegeven scheidingsteken. Ik zal hier later bij de voorbeeldapplicatie op terugkomen. --prefix : Het prefix dat aan iedere config item wordt toegekend. Stel je wilt de configuratie van meerdere applicaties in 1 Azure App Configuration opslaan, dan is het mogelijk om met een prefix de geïmporteerde items applicatiespecifiek te maken en collisions in naamgeving te voorkomen. --yes : doe een 'silent' import, vraag vooral niet om akkoord voor het importeren van items.

Het importeren van de omgeving specifieke configuratie items

az appconfig kv import --name "$(AppConfiguration)" --label "$(ASPNETCORE_ENVIRONMENT)" --source file --path $(ConfigurationDirectory)/appsettings.$(ASPNETCORE_ENVIRONMENT).json --format json --separator : --prefix "$(AppConfigurationPrefix)" --yes

Parameters zijn gelijk aan degene die hierboven beschreven staan. De $(ASPNETCORE_ENVIRONMENT) is een pipeline variabele die aangeeft voor welke stage in de releasepipeline er wordt gedeployed.

Bepalen van de connectionstring naar de Azure App Configuration

$appConfigConnectionString = $(az appconfig credential list -g "$(ResourceGroupName)" -n "$(AppConfiguration)" --query "[?name=='Primary'].connectionString" -o tsv)

Beschrijving parameters -g : De resourcegroup van de betreffende Azure App Configuration -n : De naam van de betreffende Azure App Configuration --query : De query naar de op te vragen connectionstring -o : Type output in dit geval Tab Separated Value (tsv)

Toekennen van de connectionstring aan de applicatie

az webapp config connection-string set -g "$(ResourceGroupName)" -n $(AppServiceName) -t Custom --settings MyAppConfiguration=$appConfigConnectionString

Beschrijving parameters -g : De resourcegroup van de aan te passen applicatie -n : De naam van de aan te passen applicatie -t : Type Connection string. Ondersteunde waardes zijn: _ ApiHub, Custom, DocDb, EventHub, MySql, NotificationHub, PostGreSQL, RedisCache, SQLAzure, SQLServer, ServiceBus _ --settings : KeyValue pair van de connectionstring Key : Naam van de connectionstring settings voor de Azure App Configuration, Value de in de vorige stap opgevraagde connectionstring.

Achterhalen Key Vault Id

$clientSecretVaultId = $(az keyvault secret list --vault-name $(KeyVaultName) --query "[?name=='$(ClientSecretKeyVaultKeyName)'].id" -o tsv)

Linken van een Key Vault secret

az appconfig kv set-keyvault --name $(AppConfiguration) --key "MyApp:MySection:OtherApplication:Secret" --label $(ASPNETCORE_ENVIRONMENT) --secret-identifier $clientSecretVaultId --yes

Beschrijving parameters --name : Naam van de Azure App Configuration --key : De te importeren key. Hier zie je ook mooi de eerder beschreven separator terugkomen. --label : De label toe te kennen aan het te importeren item --secret_identifier : Het in de vorige stap achterhaalde id van de Key Vault, de bron van de import. --yes : Doe een silent import, vraag vooral niet om toestemming.

Herstarten van de applicatie

az webapp restart -g "$(ResourceGroupName)" -n $(AppServiceName)

Herstart is nodig om de configuratie aanpassingen te effectueren

Het is een bewuste keuze om het vullen van de Key Vault vooral handmatig te doen, in ieder geval niet vanuit een release pipeline omdat er dan nog steeds secrets in een bestand opgeslagen moeten worden en dat is nu juist wat we niet willen. Een key Vault is een ideale plek om deze informatie op te slaan aangezien deze met Azure AD wordt beveiligd, alleen mensen waarvan jij wilt dat ze bij de Key Vault moeten kunnen geef je rechten, voor de rest van de wereld blijven het vooral secrets. Mocht je de Key Vault ook vanuit een release willen aanmaken zodat een hele omgeving geautomatiseerd is uit te rollen dan zou het vullen van de Key Vault vanuit een release pipeline met 'secret' variabelen een goed alternatief zijn.

Stel je hebt bovenstaande in een Azure CLI stap opgenomen en deze opgeslagen, dan zou je bij wijze van spreken een bestaande Azure App Configuration kunnen verwijderen, een nieuwe release uitvoeren en het eindresultaat is dat er weer een nieuwe instantie van de Azure App Configuration weer volledig gevuld wordt aangemaakt en dat de uitgerolde applicatie ook meteen gebruik maakt van deze nieuwe Azure App Configuration instantie.

Ik heb er hier bewust voor gekozen om vanuit de release van een applicatie de configuratie bij te werken, je zou dit misschien liever vanuit een eigen repository willen doen waarbij de configuratie echt los van de applicatie wordt gedeployd, maar t.b.v. de reductie van de complexiteit heb ik voor nu deze oplossing gekozen.

Maar...nu we het aanmaken en vullen van de Azure App Configuration hebben geautomatiseerd, moeten we deze informatie gaan consumeren in een applicatie. In de blog "Hoe consumeer je items uit een Azure App Configuration" lees je hoe dit werkt.

Documentatie

The Twelve-Factor App

Microsoft documentatie over Azure Application Configuration

Hoe consumeer je items uit een Azure App Configuration