Przejdź do głównej zawartości

Wzorce infrastruktury jako kod

Wzorce infrastruktury jako kod dla agentów AI to cztery receptury (podział monolitu Terraform, bramka na planie, przejęcie dryfu, utwardzenie CloudFormation lub Pulumi) wykonywane przez Claude Code, Codex albo Cursor z poświadczeniami chmurowymi tylko do planu. Maszynowo sprawdzana bramka planu, a nie człowiek czytający HCL, blokuje każde usunięcie lub zastąpienie do zgody wskazanego właściciela.

Ta strona jest dla programistów, którzy odpowiadają za infrastrukturę swojej usługi, i dla liderów technicznych, którzy decydują, czego agent może dotknąć na koncie chmurowym. Dziedziczysz repozytorium Terraform z jednym 900-liniowym main.tf, zaszytymi na sztywno blokami CIDR, providerem przypiętym dwie wersje główne wstecz i terraform plan, który chce zastąpić produkcyjną instancję RDS. Agent podzieli ten plik w kilka minut. Pytanie brzmi: skąd wiesz, że podział niczego nie niszczy, skoro plan ma 400 linii i nikt nie chce go czytać?

  • Konfigurację uprawnień, w której agent może uruchomić terraform plan, ale nie ma poświadczeń, którymi dałoby się zrobić apply
  • Krótki plan-gate.sh, który kończy się błędem przy każdym usunięciu lub zastąpieniu, więc przegląd zaczyna się od krótkiej listy, a nie od całego planu
  • Prompty do skopiowania: podział na moduły z blokami moved, przegląd planu, przejęcie dryfu blokami import i utwardzenie szablonu CloudFormation
  • Tabelę bramek (format, lint, polityki, testy, plan), które dowodzą poprawności zmiany bez czytania każdej linii
  • Kroki naprawcze dla blokady stanu, która nie chce się zwolnić, dryfu, przypadkowych zastąpień, podbicia wersji głównej providera i utkniętego wycofania CloudFormation

Zabezpieczeniem, które naprawdę trzyma, jest poświadczenie, nie prompt. Agent, który ma tylko rolę do planu, nie zrobi apply, cokolwiek postanowi uruchomić. Skonfiguruj to raz na repozytorium, w tej kolejności.

  1. Utwórz tożsamość tylko do planu. W AWS dołącz ReadOnlyAccess oraz dostęp do bucketu ze stanem: odczyt klucza stanu tego stosu i s3:PutObject / s3:DeleteObject wyłącznie na kluczu *.tflock. To wystarcza do terraform plan z natywnym blokowaniem w S3. ReadOnlyAccess daje też s3:GetObject na każdym buckecie w koncie, łącznie z plikami stanu innych stosów, więc dodaj jawny Deny na s3:GetObject dla każdego klucza stanu, który nie należy do tego stosu. Apply działa w CI na innej roli, której agent nigdy nie widzi. Ten sam podział działa w Azure (Reader plus konto magazynu ze stanem) i w Google Cloud (Viewer plus bucket ze stanem). Tożsamości tylko do odczytu dla każdej chmury opisuje strona serwery MCP dla chmur.

  2. Zablokuj destrukcyjne polecenia w narzędziu. To tylko siatka na pomyłki, nie zamiennik poświadczenia.

    W .claude/settings.json (w repozytorium, więc dostaje go cały zespół):

    {
    "permissions": {
    "allow": ["Bash(terraform fmt *)", "Bash(terraform validate *)",
    "Bash(terraform plan *)", "Bash(terraform show *)", "Bash(tflint *)"],
    "ask": ["Bash(terraform state *)", "Bash(terraform import *)"],
    "deny": ["Bash(terraform apply *)", "Bash(terraform destroy *)",
    "Bash(terraform force-unlock *)", "Bash(terraform state rm *)"]
    }
    }

    Reguły deny są sprawdzane przed ask i allow. Dopasowują tekst polecenia, więc sh -c 'terraform apply' to inna pisownia: dlaczego prawdziwą granicą są piaskownica i poświadczenie, wyjaśnia strona uprawnienia i piaskownica.

  3. Spisz konwencje raz. Umieść je w AGENTS.md (Codex go czyta; Claude Code czyta go, gdy w repozytorium nie ma CLAUDE.md, od wersji 2.1.277 w kanale latest) albo w CLAUDE.md, oraz w regule projektu Cursora:

    ## Infrastructure rules
    - Terraform >= 1.11. S3 backend with `use_lockfile = true`. No new DynamoDB lock tables.
    - Pin providers to the current major (`~> 6.0` for hashicorp/aws) and commit .terraform.lock.hcl.
    - Renames and module moves use `moved` blocks. Never `terraform state mv` in a PR.
    - Adopt existing resources with `import` blocks, not `terraform import`.
    - No hardcoded CIDRs, regions, account IDs or secrets: variables and a secrets manager.
    - You may run fmt, validate, tflint, plan and show. You never run apply or destroy.

Receptura 1: podziel monolit Terraform, niczego nie niszcząc

Dział zatytułowany „Receptura 1: podziel monolit Terraform, niczego nie niszcząc”

Podział na moduły zmienia adres każdego zasobu. Bez bloków moved Terraform czyta to jako „usuń stary adres, utwórz nowy”, co w przypadku bazy danych oznacza utratę danych. Z nimi poprawny plan czystej refaktoryzacji to zero zmian: ta jedna liczba jest twoim kryterium akceptacji.

Okno terminala
# Terminal, katalog główny repozytorium. Tryb Manual, żeby każda edycja była pokazana przed zapisem
claude --permission-mode manual

Wklej prompt poniżej. Claude Code edytuje pliki, uruchamia terraform plan i czyta jego wynik w tej samej sesji, więc pętla „zero zmian” domyka się bez kopiowania czegokolwiek.

Jeśli plan nadal pokazuje zmiany, przyczyna to prawie zawsze jedna z trzech rzeczy: brakujący blok moved, wartość domyślna inna niż stary literał albo zamiana count na for_each (indeks zmienia się z [0] na ["name"] i to też wymaga bloku moved). Poproś agenta, żeby dla każdego zasobu wskazał, która to z nich.

Receptura 2: zamień plan w bramkę, której nie czytasz linia po linii

Dział zatytułowany „Receptura 2: zamień plan w bramkę, której nie czytasz linia po linii”

Plan na 400 linii to miejsce, w którym przegląd zawodzi, bo jedno -/+ na bazie danych ginie wśród 60 zmian tagów. Odwróć kolejność: skrypt wyciąga destrukcyjne akcje z planu w formacie maszynowym, a człowiek przegląda tylko tę listę.

#!/usr/bin/env bash
# scripts/plan-gate.sh: exit 2 when the plan deletes or replaces anything
set -euo pipefail
terraform plan -out=tfplan -input=false -lock-timeout=60s
terraform show -json tfplan > plan.json
jq -r '.resource_changes[]?
| select(.change.actions | index("delete"))
| "\(.change.actions | join("/"))\t\(.address)"' plan.json > destructive.txt
jq '[.resource_changes[]? | {address, actions: .change.actions,
replace_paths: .change.replace_paths}]' plan.json > plan-summary.json
jq -r '[.resource_changes[]?.change.actions | join("/")] | group_by(.)
| map("\(.[0]): \(length)") | .[]' plan.json
if [ -s destructive.txt ]; then
echo "Delete or replace needs a named approver:"; cat destructive.txt; exit 2
fi

W terraform show -json zastąpienie pojawia się jako ["delete","create"] albo ["create","delete"], więc dopasowanie do delete łapie oba przypadki. Uruchamiaj skrypt w sesji agenta i ponownie w CI. W CI zapisz destructive.txt jako wynik zadania (job output); gdy nie jest pusty, zadanie apply trafia do chronionego środowiska, którego wymaganym recenzentem jest właściciel usługi, a apply działa wyłącznie z zapisanego i przejrzanego tfplan.

W ramach receptury 1 dodaj lifecycle { prevent_destroy = true } do zasobów stanowych (bazy danych, buckety z danymi, klucze KMS). Terraform odrzuci wtedy każdy plan, który je usuwa, więc najdroższy błąd staje się błędem planu.

Ktoś w trakcie incydentu dodał w konsoli regułę grupy zabezpieczeń i teraz każdy plan chce ją usunąć. Nie pozwól agentowi nadpisać dryfu przez apply: ta reguła może być tym, co powstrzymuje incydent.

Uruchom terraform plan -refresh-only, żeby zobaczyć dokładnie, co zmieniło się poza Terraformem. Potem zdecyduj dla każdego zasobu: przejąć go, cofnąć zmianę w konsoli czy dopasować konfigurację. Przy przejmowaniu Terraform 1.5 i nowszy zapisuje import jako kod, więc przechodzi on przegląd jak każda inna zmiana.

Serwer MCP chmury przyspiesza tę pracę: z dostępem do odczytu agent sam wylistuje prawdziwe zasoby, zamiast prosić cię o wklejenie wyników z konsoli. Podłącz go tylko do odczytu, tak jak pokazuje strona serwery MCP dla chmur.

Receptura 4: utwardź szablon CloudFormation lub stos Pulumi

Dział zatytułowany „Receptura 4: utwardź szablon CloudFormation lub stos Pulumi”

Ta sama dyscyplina działa z innym poleceniem podglądu. W CloudFormation aws cloudformation deploy --no-execute-changeset tworzy zestaw zmian (change set), który agent może przeczytać bez jego wykonania. W Pulumi planem dla człowieka jest pulumi preview --diff, a dla bramki pulumi preview --json: kroki, których op to delete, replace, create-replacement lub delete-replaced, odfiltruj do destructive.txt, tak samo jak robi to plan-gate.sh. Bramka jest ta sama: wypisz zastąpienia i blokuj do zgody właściciela.

Jak zweryfikować infrastrukturę napisaną przez agenta bez czytania każdej linii?

Dział zatytułowany „Jak zweryfikować infrastrukturę napisaną przez agenta bez czytania każdej linii?”

Ułóż kontrole tak, żeby każda mechanicznie łapała jedną klasę błędów, a człowiek patrzył tylko na to, co przez nie przejdzie. Uruchamiaj je w tej kolejności, od najtańszej, w sesji agenta i ponownie w CI.

BramkaPolecenieŁapieBlokuje build, gdy
Format i składniaterraform fmt -check -recursive i terraform validateWymyślone argumenty, błędy typów, złe referencjejest jakikolwiek błąd
Linttflint --recursiveNieistniejące typy instancji, przestarzałą składnię, nieużywane deklaracjejest jakikolwiek błąd
Polityki statycznecheckov -d .Publiczne buckety, niezaszyfrowane magazyny, otwarte grupy zabezpieczeńkontrola nie przechodzi i nie ma jej na przejrzanej liście wyjątków
Zachowanieterraform testModuł, który przestał spełniać swój kontraktktórykolwiek przebieg testu się nie powiedzie
Polityki planuconftest test plan.jsonTwoje własne reguły zastosowane do faktycznie planowanych wartościpojawi się jakikolwiek deny
Destrukcjascripts/plan-gate.shUsunięcia i zastąpieniadestructive.txt ma choć jedną linię

Wersje sprawdzone 26 września 2026: Terraform 1.16.4, provider hashicorp/aws 6.66.0, tflint 0.64.0, checkov 3.3.19, conftest 0.70.1. Polityka planu dla conftest to kilka linii Rego nad plan.json:

policy/no_public_ssh.rego
package main
import rego.v1
deny contains msg if {
some rc in input.resource_changes
rc.type == "aws_security_group_rule"
rc.change.after.type == "ingress"
"0.0.0.0/0" in rc.change.after.cidr_blocks
rc.change.after.from_port <= 22
rc.change.after.to_port >= 22
msg := sprintf("%s opens SSH to the internet", [rc.address])
}

Kto zatwierdza. Agent może stwierdzić, że kontrole są zielone. Nie może zatwierdzać. Pull request, który przechodzi wszystkie bramki i ma pusty destructive.txt, potrzebuje jednego recenzenta diffu. Pull request z jakąkolwiek destrukcyjną linią wymaga, żeby właściciel usługi zatwierdził tę listę imiennie, a apply rusza w CI z przejrzanego tfplan. Dołącz wynik bramek do pull requesta jako jego pakiet dowodów, żeby recenzent czytał sześć wyników i krótką listę zamiast HCL.

Częstym źródłem zepsutego HCL są argumenty providera, które agent pamięta ze starszej wersji głównej. Naprawiają to dwa dodatki, a pełną konfigurację opisuje strona serwery MCP dla infrastruktury.

Serwer Terraform MCP od HashiCorp bez tokenu rejestruje tylko narzędzia rejestru, które sprawdzają aktualną dokumentację providerów i modułów. Z samej konstrukcji działa więc tylko do odczytu:

Okno terminala
claude mcp add terraform -s project -- docker run -i --rm hashicorp/terraform-mcp-server:1.3.0

Umiejętności agentowe HashiCorp (hashicorp/agent-skills, MPL-2.0) zawierają konwencje: refactor-module do receptury 1, terraform-search-import do receptury 3, terraform-test i terraform-style-guide do bramek. W Claude Code zainstaluj plugin, a w Codex i Cursorze pojedyncze umiejętności przez CLI skills (1.7.0):

Okno terminala
# Claude Code
claude plugin marketplace add hashicorp/agent-skills
claude plugin install terraform@hashicorp
# Codex, Cursor i inni agenci: wybieraj umiejętności pojedynczo
npx skills add hashicorp/agent-skills/plugins/terraform/skills/refactor-module

Cały plugin ładuje 16 umiejętności i kosztuje około 2150 tokenów stale obecnych w każdej sesji (claude plugin details terraform@hashicorp, 26 września 2026); każda umiejętność dokłada 0,5k–7k, gdy się uruchomi. Szerszy przegląd znajdziesz na stronie serwery MCP dla infrastruktury. Nie instaluj dodatkowo terraform@claude-plugins-official: rejestruje drugi serwer MCP terraform przypięty do starszego obrazu 0.4.0. Sięgaj po umiejętność, gdy chcesz konwencji, a po serwer MCP, gdy agent potrzebuje aktualnych danych z rejestru.

Warunkiem wstępnym tych receptur jest środowisko pracy agenta, które trzyma granice: uprawnienia i piaskownica. Następny krok to umieszczenie bramek i apply w potoku.