# Infrastructure as code

## Hvad det er

Infrastructure as code er praksis med at beskrive det, et system kører på
(maskiner, netværk, firewallregler, DNS-poster), i tekstfiler, holde filerne i
versionsstyring og lade et værktøj bygge det rigtige miljø ud fra dem. Filerne
er originalen. En ændring af infrastrukturen begynder som en ændring i en fil,
gennemgår samme review som programkoden og når først det kørende system, når
værktøjet udfører den. På dansk skrives det også infrastruktur som kode, og
forkortelsen IaC går igen på begge sprog.

Vejledningen NIST SP 800-204C, der handler om DevSecOps for software bygget af
microservices, beskriver infrastructure as code som den kode, der opretter og
konfigurerer de ressourcer, et program rulles ud på, og gør det gentageligt og
ensartet hver gang. Ifølge samme vejledning versionsstyres definitionerne med de
samme værktøjer som programmets egen kildekode. Gentageligheden er hele pointen.
Et miljø, der kan skabes ud fra filer, kan skabes igen: ved siden af det første,
efter et nedbrud eller i en testkonto en tirsdag eftermiddag.

Definitioner findes i to hovedformer. En deklarativ definition beskriver
slutresultatet, for eksempel en server af en bestemt størrelse i et bestemt
netværk med én port åben, og overlader til værktøjet at regne ud, hvad der skal
oprettes, ændres eller lades i fred. En imperativ definition er en liste af
trin: opret dette, tilknyt hint, og åbn så porten. SP 800-204C regner
infrastructure as code til deklarativ kode: teamet skriver ned, hvad det har
brug for, og et værktøj gør det til en kørende instans. HashiCorp beskriver
Terraform-sproget på samme måde: det angiver et mål og ikke de trin, der fører
derhen. Forskellen viser sig ved anden kørsel. En liste af trin fortæller,
hvordan noget blev gjort én gang, mens en erklæring fortæller, hvad der skal
findes, og det er netop det, værktøjet skal vide, når en del af det allerede er
der.

Der findes ikke ét fælles sprog. SP 800-204C nævner, at koden alt efter
værktøjet skrives enten i et almindeligt scriptsprog som Python, JavaScript
eller TypeScript eller i værktøjets eget konfigurationssprog, for eksempel
HashiCorps HCL, som kan være forenelig med et standardformat som JSON, men ikke
behøver at være det. Terraforms sprog hører til den sidste slags og ligger i
filer, der ender på `.tf`. Azure tager imod Resource Manager-skabeloner i JSON
eller i det kortere sprog Bicep. Ansible skriver sine playbooks i YAML og når
maskinerne over SSH med deres eksisterende brugeroplysninger, uden at der
installeres en agent på dem.

Det hjælper at skelne mellem to opgaver, der begge går under navnet.
Provisionering skaber delene: den virtuelle maskine, disken, netværket,
firewallen. Konfiguration former det, der kører inde i en maskine, som allerede
findes: pakker, konti, servicefiler. Nogle værktøjer hælder mod den ene opgave
og nogle mod den anden, og grænsen er flydende. Det, der gør begge dele til
infrastructure as code, er, at definitionen ligger i et repository, og at
værktøjet bringer det rigtige system i overensstemmelse med den.

Værktøjerne husker også forskelligt, hvad de har bygget. Terraform fører en
tilstandsfil, der kobler ressourcerne i konfigurationen til de rigtige objekter,
det har oprettet, og dokumentationen kalder den tilstand sandhedskilden for
miljøet. Bicep har ingen tilstandsfil, fordi Azure selv registrerer, hvad der er
rullet ud. Hvor optegnelsen ligger, afgør, hvad der sker, når to personer kører
værktøjet samtidig, eller når optegnelsen går tabt.

## Hvorfor det betyder noget

Før infrastructure as code blev hvert miljø sat op i hånden og siden lappet i
hånden. Staging fik en rettelse, som produktion ikke fik, en port åbnet til en
test blev aldrig lukket, og én maskine ud af tre fik en nyere pakke. SP 800-204C
kalder fænomenet drift, i betydningen at noget glider væk: opsætningen i brug
giver ikke længere den tilstand, systemet var tænkt til at være i. Vejledningen
peger også på den sædvanlige vej derind, en ændring lavet i en webkonsol eller
på kommandolinjen uden om koden. Når en release virker i staging og fejler i
produktion, er det ofte den slags afvigelse, der viser sig.

Infrastructure as code flytter originalen ud af det kørende system. Teamet
ændrer definitionen, og det kørende system følger efter. Ingen logger ind for at
rette en firewallregel; man retter filen, der beskriver firewallen, og værktøjet
udfører ændringen. Det giver tre ting.

Den første er review og historik. En ændring af netværket bliver til en diff i
en pull request, som en anden person læser, før noget sker. Måneder senere har
spørgsmålet om, hvem der åbnede en port og hvorfor, et svar i commit-loggen og
ikke i nogens hukommelse. At fortryde en ændring er et revert efterfulgt af en
ny kørsel af værktøjet.

Den anden er gentagelighed. En velskrevet definition er idempotent: anvendt på
et miljø, der allerede passer, ændrer den ingenting, og anvendt på et, der er
gledet væk, bringer den det tilbage. Der er to veje dertil: enten justeres det,
der står, eller det kasseres, og et nyt bygges op fra definitionen. Den anden
vej, gjort til en fast regel, er immutable infrastructure.

Den tredje er billige miljøer. SP 800-204C beskriver værktøjerne som
automatisering af infrastrukturens hele levetid, fra provisionering til
nedlæggelse, og bemærker, at noget af den lever så kort, at det ikke kan betale
sig at vedligeholde den. Et team kan oprette flere testmiljøer efter behov og
rive dem ned igen, og selve koden kan tjekkes, før den rører noget: vejledningen
kræver, at hver eneste del af den scannes for sårbarheder, før den merges. En
gren kan få sin egen kortlivede kopi af produktionens opbygning, og en reviewer
kan se en forhåndsvisning af en ændring, før den godkendes.

Omkostningerne skal også siges lige ud. At ændre én firewallregel via en pull
request er langsommere end at klikke i en webkonsol, og under et nedbrud føles
det dyrt. Hvert værktøj har sit eget sprog og sin egen model for tilstand, som
skal læres. Og en fil, der kan bygge et miljø, kan også slette det: håndtaget
rækker lige langt begge veje, og derfor betyder forhåndsvisningen lige så meget
som kørslen bagefter.

## Sådan ser det ud i praksis

Her er én ændring på vej fra redigering til kørende system, i et setup hvor et
CI-job kører værktøjet og lægger prøvekørslen på pull requesten:

```text
Dag 1   09.10  Ret netværksdefinitionen: tillad TCP 443 fra kontoret
        09.12  Åbn en pull request; CI laver en prøvekørsel (planen)
        09.14  Planen står på pull requesten: én regel tilføjet, ingen fjernet
        10.30  En reviewer læser planen såvel som diffen og godkender
        10.31  Merge; CI udfører den plan, der blev godkendt
        10.33  En ny kørsel af samme definition melder ingen ændringer
Dag 40         Nogen spørger, hvorfor 443 er åben: commit-loggen siger hvem
```

Planen er den del, der tjener sig ind. Diffen viser, hvad forfatteren ville
ændre; planen viser, hvad værktøjet faktisk vil gøre, også det forfatteren ikke
regnede med, for eksempel en ressource, det har tænkt sig at fjerne. Terraform
kan gemme en plan i en fil med `-out`, og `terraform apply` udfører så
handlingerne i netop den fil, så det, der kører kl. 10.31, er det, revieweren
godkendte kl. 10.30. Den stille kørsel kl. 10.33 er beviset på, at definitionen
og systemet nu er enige.

Her på sitet lover
[ydelsessiden om Infrastructure as Code](/da/services/infrastructure-as-code/)
en server, der kan genopbygges på en eftermiddag, fordi hele konfigurationen
ligger i filer og ikke i nogens hukommelse.

Det sikre forløb handler mest om rækkefølge, og en
[porteføljeside fra september 2026](/da/portfolio/provisioned-a-second-server-from-code/)
viser hvorfor. Den beskriver en ekstra server, som skulle stå hos en anden
udbyder. Den tidligere maskine var sat op i hånden og først beskyttet senere, så
en nyoprettet vært, der ikke havde andet at stå imod med end en standardpolitik
for adgangskoder, var en tid tilgængelig fra internettet. Denne gang blev
serveren oprettet fra repositoryet, og beskyttelsen var på plads, før maskinen
var. Firewall-playet køres forud for server-playet, og hos den anden udbyder
tildeles taggene før cloud-firewallen, da regler rettet mod tags ikke har noget
at hæfte sig på, før taggene er der. En ny kørsel gør ingen skade, for findes
maskinen allerede, tager rollen den blot i brug i stedet for at lave en til. Den
rolle er den eneste, der koster penge, så dens egen dokumentation angiver
regionen, maskintypen med specifikation og prisen pr. måned i euro. Siden er
også ærlig om, hvad en prøvekørsel ikke fanger. Begge firewalls konfigureres ved
at kalde udbydernes REST-API'er fra en generel HTTP-opgave, og den opgave
understøtter ikke check-tilstand. Derfor lader en prøvekørsel den ligge. Den
egentlige generalprøve er en kørsel af firewall-playet alene, som intet koster
og kan fortrydes.

## Hvor det går galt

Infrastrukturkode fejler gerne på måder, der ligner succes. Værktøjet kører
færdigt uden fejl, CI-kørslen er grøn, og filerne og det rigtige system er
stille og roligt uenige.

**En hurtig rettelse under en hændelse bliver fjernet af næste kørsel.** Nogen
åbner en port i konsollen klokken to om natten, nedbruddet er overstået, og en
uge senere lukker den faste kørsel porten igen, fordi filerne aldrig hørte om
den. Det modsatte sker også: ingen kører værktøjet igen, og filerne holder op
med at beskrive virkeligheden. Skriv nødrettelsen ind i koden samme dag, og kør
et planlagt tjek, der sammenligner filerne med det, der kører; SP 800-204C beder
om den sammenligning lige efter hver udrulning og med jævne mellemrum, mens
systemet kører. Med Terraform gør plan-kommandoens detaljerede exitkode det til
et kort natligt job:

```sh
terraform plan -detailed-exitcode
# exit 0: ingen ændringer, exit 1: fejl, exit 2: ændringer fundet
```

På en gren, der allerede er udført, betyder exitkode 2, at det rigtige system
har bevæget sig væk fra koden, og at nogen bør finde ud af hvorfor.

**At slette en ressource fra filen sletter den ikke altid.** Værktøjerne er
uenige om, hvad en manglende ressource betyder. For Azure Resource
Manager-skabeloner skriver Microsoft i dokumentationen til what-if, at en
ressource, der findes, men ikke står i skabelonen, kun slettes i complete mode.
I andre tilstande kører den videre og koster fortsat penge. Lær dit værktøjs
regel for fjernelse, før du stoler på den, og læs forhåndsvisningen for de
sletninger, du forventer, såvel som dem, du frygter.

**Prøvekørslen ser ikke alt.** En forhåndsvisning er en model af ændringen,
ikke en garanti. Microsofts what-if-dokumentation nævner egenskaber, der meldes
som slettet, selv om Azure blot udfylder en standardværdi under udrulningen, og
kalder resultatet støj; enhver egenskab, der sættes via `reference`-funktionen,
vises som ændret ved hver kørsel. Porteføljesiden ovenfor viser det modsatte
hul: en opgave uden understøttelse af check-tilstand springes over i en
prøvekørsel, så forhåndsvisningen siger intet om den. Kend dit værktøjs kendte
støj, og betragt alt, hvad forhåndsvisningen ikke kan se, som utestet, indtil
en afprøvning på noget billigt, der kan rulles tilbage, har dækket det.

**Hemmeligheder havner i repositoryet eller i tilstanden.** En adgangskode
skrevet ind i en definitionsfil bliver liggende i historikken i hver eneste
klon. Mindre oplagt: Terraforms dokumentation advarer om, at tilstanden kan
indeholde hemmeligheder, og som standard er den tilstand en lokal fil ved navn
`terraform.tfstate`. Hold hemmeligheder i et hemmelighedslager og henvis til dem
ved navn, læg tilstanden i fjernlager med låsning og adgangskontrol, som
HashiCorp anbefaler, og ret aldrig tilstandsfilen i hånden; kommandoen
`terraform state` findes til de ændringer, der er brug for.

**Et script forklædt som definition.** Et shellscript i repositoryet, der
opretter en server, er versionsstyret, men det er ikke idempotent: kør det to
gange, og der står to servere, eller en fejl midt i det hele. Brug deklarative
ressourcer eller trin, der tjekker, før de handler, som rollen ovenfor, der
overtager en eksisterende maskine. Ansibles dokumentation sætter barren klart:
når et system allerede er i den tilstand, en playbook beskriver, ændrer en ny
kørsel af playbooken ingenting.

## Læs mere

- [Implementation of DevSecOps for a Microservices-based Application with Service Mesh (NIST SP 800-204C)](https://csrc.nist.gov/pubs/sp/800/204/c/final)
  — deklarativ, versionsstyret infrastrukturkode, afvigelser og scanning.
- [Terraform Language Documentation](https://developer.hashicorp.com/terraform/language)
  — det deklarative sprog, dets ressourcer og dets blokke.
- [Command: plan](https://developer.hashicorp.com/terraform/cli/commands/plan)
  — prøvekørslen, gemte planer og de detaljerede exitkoder.
- [State](https://developer.hashicorp.com/terraform/language/state)
  — hvad Terraform registrerer, hvor det ligger, og hvorfor det er følsomt.
- [Introduction to Ansible](https://docs.ansible.com/ansible/latest/getting_started/introduction.html)
  — agentløs automatisering over SSH, playbooks og ønsket tilstand.
- [Template deployment what-if](https://learn.microsoft.com/en-us/azure/azure-resource-manager/templates/deploy-what-if)
  — forhåndsvisning af en Azure-udrulning, ændringstyperne og den kendte støj.

<https://engineer.company/da/glossary/infrastructure-as-code/>
