Infrastructure as Code

Terraform

En praktisk genomgång av hur Terraform modellerar infrastruktur med providers, resurser, moduler och state.

Terraform är HashiCorps verktyg för infrastruktur som kod, som gör att team kan definiera moln- och lokala resurser i lättlästa konfigurationsfiler och sedan skapa, uppdatera och ta bort dessa resurser via ett automatiserat flöde. Verktyget använder en deklarativ modell: du beskriver det önskade sluttillståndet för infrastrukturen snarare än stegen för att nå dit, och Terraform avgör vilka åtgärder som behövs varje gång du kör plan eller apply. I DevSecOps är Terraform grundläggande eftersom det för in infrastrukturändringar i samma disciplin för kodgranskning, testning och granskning som gäller för applikationskod, vilket gör säkerhetsregler möjliga att tillämpa automatiskt innan någon resurs skapas.

Lärandemål

Det här ska du kunna efter genomläsning.
  • Förstå providers, resurser, variabler och outputs som kärnmodellen för konfiguration.
  • Förklara hur state och remote state stödjer kontrollerad leverans.
  • Använda plan och apply som ett avsiktligt tvåstegsflöde.

I korthet

En snabb mental modell innan du går på djupet.
Konfigurationsmodell
  • Providers
  • Resurser
  • Variabler
Återanvändning och sammansättning
  • Moduler
  • Outputs
  • Versionsstyrda gränssnitt
Ändringsflöde
  • Plan
  • Apply
  • Remote state

Kärnidén

Terraform omvandlar infrastrukturens avsikt till deklarativa konfigurationsfiler. Du beskriver vad som ska finnas, inte hur det ska skapas. Vid varje körning läser Terraform konfigurationen, jämför den med det registrerade tillståndet och producerar en precis ändringsplan. Denna uppdelning mellan avsikt och utförande är det som gör Terraform både kraftfullt för automatisering och säkrare för granskning.

Den modellen kräver att tre saker fungerar väl tillsammans. Providers som översätter Terraforms resursmodell till verkliga API-anrop, state som spårar vad Terraform redan förvaltar och ett disciplinerat flöde där varje ändring passerar plan innan den berör live-infrastruktur.

Operativt mönster

  • Använd providers för att koppla Terraform till de system det förvaltar och pinna provider-versioner så att beteendet förblir förutsägbart i teamet.
  • Använd resurser för att beskriva de konkreta objekt du vill skapa eller ändra, och håll varje resursdefinition smal och läsbar.
  • Använd moduler för att hålla återkommande infrastrukturmönster snygga, återanvändbara och centralt underhållna.

Basnivå

  • Håll planer tillräckligt små för att granskare fullt ut ska kunna förstå konsekvenserna innan de godkänner.
  • Behandla state-backenden som en del av kontrollplanet. Skydda den med åtkomstkontroller, kryptering och versionshantering.
  • Föredra tydliga indata och utdata i moduler så att beteendet förblir begripligt och gränssnittet stabilt.

Signaler att bevaka

Mönster som är värda att undersöka vidare.
  • Planer innehåller många orelaterade ändringar som är svåra att förklara.
  • State lagras på ett okontrollerat ställe eller delas för brett.
  • Moduler döljer för mycket beteende bakom ett enkelt gränssnitt.

FÖRDJUPNING

Providers

En provider är ett plugin som översätter Terraforms resursmodell till anrop mot ett specifikt API. AWS-providern vet hur man skapar S3-buckets, IAM-roller och VPC:er. Kubernetes-providern vet hur man hanterar namnrymder, deployments och services. Utan en provider kan Terraform inte kommunicera med en plattform. Providers distribueras separat från Terraform och hämtas under terraform init baserat på required_providers-blocket i konfigurationen.

Versionering av providers är en kritisk operativ praxis. Att pinna providers till en specifik version eller ett begränsat intervall i required_providers-blocket säkerställer att teamet, CI-pipelines och automatiserade applies alla använder identiskt provider-beteende. En opinnnad provider innebär att körning av terraform init på en ny maskin kan hämta en nyare provider-version med annat beteende eller nya obligatoriska fält.

Provider-konfiguration inkluderar vanligtvis autentiseringsuppgifter och en målregion eller endpoint. I produktionspipelines bör autentisering komma från miljövariabler eller en workload identity-mekanism snarare än hårdkodade uppgifter i provider-blocket. Att lagra molnuppgifter i Terraform-konfigurationsfiler omöjliggör god secrets-hantering.

Ett vanligt misstag är att behandla providern som ett fast bakgrundselement och uppgradera den sällan. Stora provider-versionsuppgraderingar byter ofta namn på argument, tar bort föråldrade resurser eller ändrar hur befintliga resurser tolkas. Uppgradering av providers bör behandlas som uppgradering av ett beroende. Görs avsiktligt, testas i icke-produktionsmiljö och granskas mot providerloggboken.

Resurser

Ett resource-block är den grundläggande enheten i Terraform-konfiguration. Det deklarerar ett enskilt infrastrukturobjekt av en specifik typ, exempelvis aws_s3_bucket eller kubernetes_deployment, och specificerar de attribut som definierar objektets konfiguration. Terraform kopplar varje resource-block till exakt ett verkligt objekt i målsystemet, och spårar relationen i state-filen.

Bra resursdefinitioner är smala, fokuserade och självbeskrivande. En resurs som konfigurerar tio distinkta beteenden i ett enda block är svår att granska. Att dela logiskt separata bekymmer i distinkta resurser gör varje ändring mindre, enklare att granska och lättare att isolerat testa.

Distinktionen mellan oföränderliga och föränderliga resurser är viktig vid granskning av planer. Vissa attribut kan uppdateras i befintlig resurs. Andra attribut är oföränderliga och tvingar Terraform att ta bort den gamla resursen och skapa en ny. Planen markerar dessa med -/+, och granskare bör behandla varje ersättning av en tillståndsfull resurs med stor försiktighet.

Resursberoenden i Terraform härleds vanligtvis automatiskt. Om resurs B refererar till ett attribut från resurs A skapar Terraform A innan B och tar bort B innan A. Explicit depends_on finns när ett beroende existerar på en högre nivå än Terraform kan detektera, men det bör användas sparsamt för att hålla beroendegraferna transparenta.

Variabler

Variabler parametriserar en Terraform-konfiguration så att samma kod kan producera olika utfall i olika miljöer, regioner eller användningsfall utan att duplicera logik. En variabel har ett namn, en valfri typbegränsning, en valfri beskrivning och ett valfritt standardvärde. Anropare tillhandahåller värden via variabelfiler, miljövariabler med prefixet TF_VAR_ eller interaktiva frågor.

Typbegränsningar gör variabelgränssnitt självdokumenterande och fångar misstag tidigt. Att deklarera en variabel som type = string, type = number eller type = object({...}) ger Terraform informationen att validera indata innan någon plan körs. Utan typer kanske ett felaktigt variabelvärde inte framgår som ett fel förrän Terraform försöker använda det i ett API-anrop.

Känsliga variabler bör markeras med sensitive = true, vilket förhindrar att Terraform skriver ut värdet i plan- eller apply-utdata. Dock förhindrar sensitive = true inte att värdet visas i state-filen. Variabler som innehåller autentiseringsuppgifter bör inte skickas direkt som Terraform-variabelvärden om state-filen inte är tillräckligt skyddad.

Ett vanligt misstag är att samla för många valfria variabler med komplexa standardvärden, vilket förvandlar en modul till ett konfigurationslabyrinth. Målet med variabler är att exponera bara de beslut som anroparen behöver fatta. Om en variabel nästan alltid lämnas på standardvärdet är den troligen ett implementeringsdetalj som bör vara intern.

Outputs

Output-värden exponerar valda attribut från en Terraform-konfiguration så att andra konfigurationer, skript eller teammedlemmar kan konsumera dem. En modul som skapar ett VPC kan exponera VPC-ID:t, subnet-ID:n och säkerhetsgrupp-ID:n. Den anropande konfigurationen använder dessa outputs för att koppla andra resurser till nätverket utan att behöva känna till VPC-modulens interna implementering.

Outputs är en del av en moduls publika kontrakt. Att byta namn på eller ta bort en output är en brytande förändring för allt som beror på den. För brett använda interna moduler bör outputs versionshanteras och ändras med samma omsorg som ett API-kontrakt i applikationskod.

Remote state-datakällor använder outputs som sin mekanism för delning mellan konfigurationer. En konfiguration kan deklarera en terraform_remote_state-datakälla som läser state-filen för en annan konfiguration. Detta skapar ett tätt beroende. Den konsumerande konfigurationen kan inte planera framgångsrikt om den producerande konfigurationens state saknas eller har tagit bort en output som konsumenten förlitar sig på.

Känsliga outputs bör markeras med sensitive = true av samma skäl som känsliga variabler. En vanlig missuppfattning är att känsliga outputs är skyddade i state-filen. Det är de inte. Flaggan förhindrar utskrift i CLI-utdata, men värdet finns fortfarande i state-filen i klartext. Att skydda känsliga outputvärden kräver att state-backenden skyddas med lämpliga åtkomstkontroller och kryptering.

State

Terraform state-filen är posten som kopplar varje resource-block i konfigurationen till det faktiska objekt den förvaltar i molnet eller på plattformen. Den lagrar resurs-ID:n, alla kända attributvärden, metadata om provider-versioner och beroendegrafiken. Utan state-filen kan Terraform inte avgöra vilka verkliga objekt det förvaltar, så varje plan skulle försöka skapa allt från grunden.

State-filer innehåller känslig information som ofta underskattas. Resurs-ARN:er, IP-adresser, databasanslutningssträngar och ibland faktiska hemligheter visas i state-filen i klartext. Alla med läsåtkomst till state-filen har en detaljerad inventering av infrastrukturen. State måste lagras i en säker fjärrbackend med kryptering och strikt åtkomstkontroll. Att lagra state i en lokal fil eller committa den till versionskontroll är allvarliga säkerhetsmisstag.

Tillståndslåsning förhindrar att parallella Terraform-operationer korrumperar state-filen. Om två operatörer eller CI-jobb kör terraform apply samtidigt mot samma state kan de producera motstridiga ändringar. Fjärrbackends som stöder låsning förvärvar ett exklusivt lås innan operationer som modifierar state. Ett gammalt lås från en kraschad operation kan blockera framtida körningar och kan kräva manuell rensning.

Manuella state-manipuleringskommandon är kraftfulla men farliga. De modifierar state-filen direkt på sätt som kringgår normal plan-och-apply-validering. Ett misstag i ett state mv-kommando kan frånkoppla en verklig resurs från Terraform-förvaltning. Alla manuella state-operationer bör föregås av en state-backup via terraform state pull, utföras noggrant och följas av en terraform plan för att verifiera konsekvens.

Remote state

Remote state låter en Terraform-konfiguration läsa outputs från en annan konfiguration lagrad i en delad backend. Datakällan terraform_remote_state läser state-filen för en målkonfiguration och exponerar dess outputs som attribut. Detta är standardmönstret för att koppla ihop oberoende konfigurationer, till exempel en nätverkskonfiguration som skapar VPC:er och en applikationskonfiguration som placerar arbetslaster i dessa nätverk.

Remote state skapar beroenden mellan konfigurationer. Den konsumerande konfigurationen kan inte framgångsrikt planera eller tillämpa om den producerande konfigurationens state saknas, är låst eller har tagit bort en output som konsumenten förlitar sig på. Detta beroende är ibland lämpligt, som när nätverksteamet äger VPC:n och applikationsteamet äger arbetslasterna med en tydlig gräns dem emellan.

Säkerhetskonsekvenserna av remote state förtjänar explicit uppmärksamhet. Backend-autentiseringsuppgifterna som används för att läsa en remote state-fil måste ha åtkomst till den state-filen, som kan innehålla känslig information. En konfiguration som läser ett annat teams state får implicit läsåtkomst till allt i det state:t. Gränssnittet mellan konfigurationer via remote state bör vara så smalt som möjligt.

Ett alternativ till remote state för att dela värden mellan konfigurationer är att använda ändamålsbyggda delade tjänster, som AWS SSM Parameter Store eller HashiCorp Consul. Detta frikopplar producent- och konsumentkonfigurationerna till priset av ett extra beroende. För stabila, sällan ändrade värden som VPC-ID:n är detta tillvägagångssätt ofta mer hållbart på sikt.

Moduler

En modul är en behållare för en grupp relaterade Terraform-resurser som kan anropas från andra konfigurationer. Moduler är det primära mekanismen för att bygga ett standardiserat internt bibliotek av infrastrukturmönster som team kan konsumera utan att behöva förstå alla implementeringsdetaljer.

Modulversionering är nödvändig för att upprätthålla ett stabilt internt bibliotek. Moduler publicerade till ett internt artefaktlager eller det offentliga Terraform Registry bör följa semantisk versionshantering. Anropare bör pinna till en specifik versionsbegränsning snarare än en rörlig referens. Säkerhetsrelevanta standardvärden bör vara säkra som standard i modulen.

Gränssnittsdesignprincipen för moduler är att exponera bara det anroparen måste kontrollera och dölja allt annat. En modul med femtio invartiabler ger ingen meningsfull abstraktion. Ett väldesignat modul har ett litet, stabilt gränssnitt som svarar på en enda tydlig fråga och hanterar alla interna resurskopplingar, taggningar och säkerhetsstandarder.

Att testa moduler är nödvändigt för moduler som används av många team. Terratest tillhandahåller ett Go-baserat integrationstestramverk som kan tillämpa en modul på en verklig molnmiljö och göra påståenden om den resulterande infrastrukturen. Checkov och OPA kan tillhandahålla snabbare policytester som validerar moduloutputs mot säkerhetsregler utan att driftsätta i en verklig miljö.

Plan och apply

Plan-steget ber Terraform att beräkna och visa varje ändring som skulle resultera av att tillämpa den nuvarande konfigurationen mot det nuvarande state:t. Utdata visar resurser som ska skapas (+), tas bort (-) och uppdateras på plats (~). För ersättningar används -/+ för att signalera att den gamla resursen tas bort och en ny skapas. Denna utdata är det primära verktyget för mänsklig granskning innan en ändring berör live-infrastruktur.

Att läsa en planresultat är en färdighet. Granskare bör titta bortom radantal och identifiera. Resursersättningar i tillståndsfulla tjänster (en databas som ersätts är nästan alltid högrisk), stora antal oväntade resurser som dyker upp eller försvinner, ändringar av säkerhetskänsliga attribut som IAM-policyer och krypteringsinställningar.

I automatiserade pipelines körs terraform plan vid varje pull request och utdata publiceras som en kommentar för granskare. Apply-steget körs bara efter att PR:n godkänts och slagits ihop med den skyddade grenen. Verktyg som Atlantis och Terraform Cloud implementerar denna modell med ytterligare funktioner som state-låsning under apply och granskningshistorik för varje plan och apply.

Att tillämpa utan att granska planen är en av de farligaste metoderna i Terraform-flöden. Det är lätt att anta att en liten ändring har en förutsägbar och begränsad effekt, men Terraform-resursers livscykler kan producera stora kaskadändringar från små konfigurationsrädigeringar. Team som kör terraform apply direkt utan att granska planresultaten arbetar utan det primära säkerhetsverktyget som verktyget erbjuder.