Chapter 08 · Week 8

Infrastructure as Code, and the Limits of the Servant

What work is rightly given to the servant, and what must the master keep?

Chapter 8 — Infrastructure as Code, and the Limits of the Servant

“Trust, but verify.” — Russian proverb, popularized in English by Ronald Reagan

“The simple believes everything, but the prudent gives thought to his steps.” — Proverbs 14:15 (ESV)


Why This Matters

For seven weeks you have administered systems by doing: you partitioned the disk, you wrote the firewall rule, you built the container, you ran the model. Real work, by hand, on a real box. This week the unit of work changes. You stop administering servers and start administering the description of servers. The infrastructure becomes a file in version control — declarative, reviewable, repeatable — and the act of administration becomes the act of writing, reading, and gating code that builds infrastructure. This is Infrastructure as Code, and it is the hinge on which everything in Phase 2 turns.

It is also the week the two threads of this book cross and lock. Until now, “AI as the tool you wield” and “AI as the workload you govern” have mostly been separate rooms. Here they become the same room. Because the tool you reach for — an AI coding agent that writes your Ansible and your Terraform — is itself a piece of governed infrastructure that can take real action on production. When you ask Claude Code or the GitHub Copilot coding agent to “set up the web tier,” you are simultaneously using AI as a tool and deploying an autonomous agent into your change pipeline. The copilot that drafts the playbook is the same kind of thing as the agent that could, if you let it, terraform apply at 2 a.m. The blessing and the danger are one object.

And that object is fast, confident, and sometimes wrong. An AI will hand you a forty-line playbook in four seconds. It will look right. It will lint mostly clean. And buried in it will be a module that does not exist, a port left open to 0.0.0.0/0, a secret in plaintext, and two tasks in the wrong order — each emitted with exactly the same fluent confidence as the forty correct lines around it. The model has no idea which of its lines are true. That is not a bug you can patch; it is the nature of the thing. The OWASP GenAI Security Project gives this its proper name — LLM05: Improper Output Handling — and the discipline it demands is ancient: the prudent gives thought to his steps.

So this week’s apologetic question is the load-bearing one for the whole back half of the course: what work is rightly given to the servant, and what must the master keep? A servant can fetch, draft, propose, and tire­lessly type. A servant cannot be accountable. When the playbook opens SSH to the world, the model is not who answers for it — you are. Luke 17:10 frames the servant’s posture exactly: “So you also, when you have done all that you were commanded, say, ‘We are unworthy servants; we have only done our duty.’” The servant does the duty; the master keeps the judgment, the verification, and the accountability. Get that boundary wrong and you have built a fast machine for shipping mistakes. Get it right and you have the most productive change pipeline of your career.

This is the midterm week. Everything from Weeks 1–7 — OS, identity, storage, network, virtualization, containers — was the foundation you now describe in code. The project (§8.13) is a 60-minute, closed-AI, open-textbook build that proves you can produce, verify, and trust infrastructure code without an agent doing your thinking for you. You earn the right to direct the servant by first proving you can do the work the servant does.


8.1 — Declarative vs Imperative: Describing the End, Not the Steps

The first mental shift in IaC is from imperative to declarative. An imperative script says how: run these commands, in this order, and hope the box was in the state you assumed. A declarative spec says what: here is the desired end state — a package present, a service running, a file with these contents — and a tool figures out the steps to reach it from wherever the box currently is.

# Imperative (a bash script): a sequence of HOW. Brittle if re-run.
apt-get install -y nginx
systemctl start nginx
echo "server { listen 443; }" > /etc/nginx/sites-enabled/site.conf
# Declarative (Ansible): a description of WHAT. Safe to re-run.
- name: nginx is present
  ansible.builtin.package: { name: nginx, state: present }
- name: nginx is running
  ansible.builtin.service: { name: nginx, state: started, enabled: true }

The difference is not stylistic. Run the bash script twice and echo clobbers your config a second time; run a slightly-different version and you have no record of what the box looked like before. Run the Ansible twice and the second run does nothing — every task reports ok instead of changed — because the desired state is already met. That property has a name, and it is the most important word in this chapter.


8.2 — Idempotency, Config Drift, and the Source of Truth

Idempotency means applying the same operation any number of times yields the same result as applying it once. A light switch is not idempotent (“flip it” toggles); a light switch labeled “on” is (“set it on” is the same whether it was on or off). Declarative IaC tools are built around idempotency: each run converges the system toward the declared state and stops.

Why you care: config drift. A box configured by hand, then patched by one engineer at midnight and another on Tuesday, slowly drifts away from any documented state. Six months later nobody can say what is actually installed, and the “works on the old server, fails on the new one” bug is born. IaC kills drift by making the file in git the single source of truth: the running system is supposed to match the declared system, and you can prove it by re-running and watching for changed (drift detected) versus ok (in sync).

ConceptImperative shellDeclarative IaC
Sayshow (steps)what (end state)
Safe to re-run?usually noyes — idempotent
Detects drift?noyes (changed vs ok)
Source of truththe boxthe file in git
Rollbackmanual, ad hocrevert the commit, re-apply

Coach’s Note — “Idempotent” is the word that separates a script from infrastructure. Before you ship any task, ask: if this runs twice, does it do harm the second time? The AI-generated ufw allow in code/webserver.yml uses shell: with no creates: guard — it is not idempotent, and it will re-open the firewall and re-log a changed on every single run. That one detail tells you the model didn’t think about convergence. You have to.

In practice you detect drift by running the tool in a no-op mode and reading the report. Ansible’s --check --diff converges nothing but tells you exactly what would change:

ansible-playbook site.yml --check --diff
# ... TASK [nginx config] ...
# changed: [web01]   <-- DRIFT: the live config no longer matches git
# ... PLAY RECAP ...
# web01 : ok=6  changed=1  unreachable=0  failed=0

A changed=1 on a --check run is the system telling you a human (or an unmanaged process) edited the box behind the IaC’s back. The right response is not to hand-edit the box back — it’s to fix the file in git and re-converge, so the source of truth and the system agree again. Terraform expresses the same idea with terraform plan: a non-empty plan on an unchanged config means the world drifted from your state. Drift detection is just idempotency used as a measurement instead of an action.


8.3 — The Two Tools: Ansible and Terraform

You will meet a hundred IaC tools in your career. Two anchor the field, and they divide the work cleanly.

Ansible is configuration management: it brings existing hosts to a desired state — install packages, write configs, start services. Agentless (it pushes over SSH), procedural-feeling but declarative in its modules, organized into playbooks of tasks. Reach for Ansible when the boxes already exist and you need to configure them.

Terraform (and its API-compatible fork OpenTofu, now under the Linux Foundation) is provisioning: it creates and destroys the resources themselves — cloud VMs, networks, buckets, DNS records — from a declarative .tf description, tracking what it built in a state file so it knows the delta between desired and actual. Reach for Terraform when you need to bring infrastructure into existence.

# Terraform: provision the thing.
resource "aws_instance" "web" {
  ami           = var.ami_id
  instance_type = "t3.small"
  tags          = { Name = "chapel-web" }
}
# Ansible: configure the thing Terraform provisioned.
- name: web tier is configured
  hosts: web
  tasks:
    - ansible.builtin.package: { name: nginx, state: present }

The common pattern: Terraform builds the house, Ansible furnishes it. Both keep their definition in git; both run in CI. And — crucially for this week — both are exactly the kind of structured, pattern-heavy text that LLMs are eerily good at generating and eerily bad at getting entirely right.


8.4 — AI as the Tool: Generating IaC, and Why You Read Every Line

Here is the tool thread, in its full glory and full danger. As of 2026, AI coding agents do not just autocomplete — they take an assigned task and produce a finished, asynchronous pull request in a sandboxed environment. The GitHub Copilot coding agent is scoped to copilot/* branches (it cannot push to main or any protected branch), and every PR it opens is automatically scanned by CodeQL and secret scanning before a human ever reviews it. Claude Code (and the Claude Agent SDK) and Cursor work the same way: assign, generate, propose. This is the reference architecture for AI-generated IaC, and it is worth memorizing as a single sentence:

AI proposes, CI validates, the human gates.

Three distinct stages, three distinct trust levels. The AI’s output is untrusted by default. Deterministic CI — linters, validators, policy scanners, secret scanning — is the cheap, tireless first filter. And the human is the irreplaceable last one, because only the human is accountable.

To make this concrete, open code/webserver.yml. It is a real-looking Ansible playbook, exactly the sort of thing an AI hands you when you say “set up an nginx web server with a firewall.” It lints mostly clean. It will deceive you if you skim. It contains, by design, the four failure classes you will see for the rest of your career:

  1. A hallucinated module. The last task calls community.general.ufw_rule. That module does not exist. The real one is community.general.ufw. The model pattern-matched a plausible name. This is the IaC face of LLM05 — confident, fluent, wrong.
  2. An insecure default. It opens SSH with ufw allow from 0.0.0.0/0 to any port 22 — the whole internet, straight to your shell. Plausible-looking, catastrophic.
  3. A plaintext secret. password: "P@ssw0rd123" sits in the user task, in the file, in git, forever.
  4. A wrong ordering / non-idempotent step. It starts nginx before the config exists, and uses a bare shell: for the firewall (no creates:), so it reports changed on every run.

Now run the deterministic gate from code/verify_iac.sh:

chmod +x code/verify_iac.sh
./code/verify_iac.sh code/webserver.yml .
# ==> 2/4  Ansible syntax + lint (catches hallucinated modules)
# ==> 4/4  Secret + danger-pattern scan (the human-gate tripwire)
#     FAIL: danger pattern found (plaintext secret / world-open ...)
# BLOCKED — fix findings, do not apply.

ansible-lint catches the hallucinated FQCN; the danger-pattern grep catches the open port and the plaintext secret. But notice what the linter cannot catch: whether opening port 22 to the world is wrong for this organization. A linter doesn’t know your policy is TLS-only. That judgment is yours. The machine finds the broken; the human finds the wrong.

There is a second, quieter failure mode worth naming: the AI is often more fluent than it is current. It will confidently use a module argument that was renamed two releases ago, a provider syntax that’s deprecated, or a default that was sane in 2022 and is a finding in 2026 — because its training distribution averages over years of the internet, not your pinned tool versions. This is why the funnel pins versions (§8.6) and why “it worked when the AI suggested it” is never an answer. The model’s confidence is uniform across its correct and its stale lines alike; only your pinned, deterministic tooling tells the difference.

Coach’s Note — The most dangerous AI-generated config is not the one that fails to lint. It’s the one that lints perfectly and is still wrong for your context — the right answer to a question you didn’t ask. AI raises your speed and your floor (it won’t forget the obvious) but it cannot raise your ceiling of judgment. That ceiling is the thing the master keeps.


8.5 — Prompt Engineering for Automation

If you must use the servant, instruct it well. Vague prompts produce vague, dangerous configs; specified prompts produce reviewable ones. The discipline of prompting for automation is the discipline of handing over your constraints explicitly, because the model will fill every gap you leave with a plausible guess.

A weak prompt: “Write Ansible to set up a web server.” You will get plaintext HTTP, an open firewall, and a hallucinated module — because you specified nothing, so it guessed everything.

A strong prompt names the target, the policy, the non-negotiables, and the form of the answer you will check:

Write an Ansible playbook for Ubuntu 24.04 that:
- installs and enables nginx, listening on 443 only (TLS-only org policy);
- pulls the cert path from a variable, never an inline secret;
- opens ONLY 443 via community.general.ufw (use the FQCN; this exact module);
- is fully idempotent — no bare shell/command without creates: or changed_when;
- writes the nginx config BEFORE starting the service.
Output the playbook only. I will run ansible-lint and read every line.

Three rules carry most of the weight. Constrain explicitly — state the policy the model can’t infer (TLS-only, least-privilege, no inline secrets). Demand idempotency by name. And tell it you will verify — not because the model “tries harder,” but because saying it out loud keeps you honest about the gate. The prompt is not where trust is established. It is where you reduce how much the gate has to catch.

Coach’s Note — A good prompt is a spec, and writing a spec is a senior skill the AI does not relieve you of — it sharpens your need for it. The engineers who get the most from coding agents are not the ones with clever incantations; they’re the ones who can state, precisely, what “correct” means for this system, because they’ve built it by hand before (which is exactly what Phase 1 is for). A vague prompt outsources your judgment to a model that has none. A precise prompt keeps the judgment with you and lets the servant do the typing.


8.6 — Validating and Correcting AI-Generated Configs

Validation is layered, cheapest-and-most-deterministic first, judgment last. Treat it as a funnel every artifact falls through before apply:

LayerTool / methodCatchesDeterminism
1. Parseyaml.safe_load, terraform validatemalformed syntaxtotal
2. Lintansible-lint, tflint, terraform fmthallucinated modules, anti-patterns, styletotal
3. PolicyOPA/Conftest, tfsec/checkov, secret scanningopen ports, public ACLs, plaintext secretstotal (to your rules)
4. Dry-run--check/--diff, terraform planwhat would actually changetotal
5. Reviewa human reading the diffwrong for this org, intent, accountabilitynone — that’s the point

Layers 1–4 are deterministic: linters and validators do not hallucinate, which is precisely why they are the antidote to a partner that does. Run them in CI on every push; they are nearly free and they never get tired or overconfident. Open code/storage.tf and run the Terraform path of code/verify_iac.sh: terraform validate flags the hallucinated auto_tiering argument, tflint/tfsec flags the public-read ACL and the missing encryption, and the danger grep catches the public ACL again. Four problems, found by machines, before a human spends a second.

But Layer 5 is where the chapter lives. A human reads terraform plan and asks the questions no linter can: Is us-east-1 actually where this data is allowed to live? Should backups really expire in 30 days? Is “chapel-backups” a bucket name that will collide? The dry-run shows you what will change; only you can decide whether it should. Never apply a plan you have not read. That is the whole ethic of this chapter compressed to six words.


8.7 — AI as the Workload/Risk: Excessive Agency and the Lethal Trifecta

Now the workload thread. An IaC agent is not a passive author; the moment you give it a tool — the ability to run terraform apply, open a PR, call an API — it becomes an autonomous actor in your production change pipeline, and the security model changes entirely. Two OWASP LLM risks govern it.

LLM06 — Excessive Agency. This is the risk of granting the agent too much permission or autonomy: write access to main, the ability to destroy infrastructure, broad cloud API scopes. The danger is that a single successful prompt injection then causes real damage — because the agent could actually do it. The control is the oldest one in security: least privilege. Scope the agent to copilot/* branches it cannot merge. Give it a read-only cloud role for planning and require a separate, human-held credential for apply. An agent that cannot destroy infrastructure cannot be tricked into destroying it.

The lethal trifecta. Coined by Simon Willison, this names the architecturally dangerous combination: (1) access to private data, (2) exposure to untrusted content, and (3) an exfiltration vector (a way to send data out). An IaC agent that reads your private repo (1), ingests an issue or a dependency’s README containing a hidden instruction (2), and can open an outbound PR or call an API (3) has all three. Then indirect prompt injection — a malicious instruction hidden in content the agent reads, not types — can make it act against you. Meta’s “Agents Rule of Two” is the practical guardrail: an unsupervised agent should satisfy at most two of those three legs; if a task needs all three, a human must be in the loop. Prompt injection is not a bug you patch — it is an architectural property you design around.

Coach’s Note — “Prompt injection is the SQL injection of the AI era.” The instinct is identical: never trust input as code. SQL injection happens when untrusted input reaches the query engine unescaped; prompt injection happens when untrusted content reaches the model as if it were instruction. The defenses rhyme too — trust boundaries, least privilege, output validation. If you internalized “never concatenate user input into SQL,” you already have the reflex. Apply it to everything the agent reads.


8.8 — CI for Infrastructure: The Pipeline That Makes Trust Cheap

The reason “AI proposes, CI validates, the human gates” is practical and not just aspirational is that CI makes the validation step nearly free and impossible to skip. A minimal infra-CI pipeline runs on every push, before any human bothers to look:

# .github/workflows/iac.yml  (sketch)
on: { pull_request: { branches: [main] } }
jobs:
  validate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - run: terraform fmt -check && terraform validate
      - run: tflint --recursive
      - uses: aquasecurity/tfsec-action@v1        # policy: no public buckets, etc.
      - run: ansible-lint playbooks/
      - run: gitleaks detect --no-banner           # secret scanning
      - run: terraform plan -out=plan.tfplan        # the diff a human will read

Two design rules make this trustworthy. First, the agent’s branch is isolated (the copilot/* pattern): the agent can open the PR but the pipeline + branch protection mean it cannot merge itself. Second, apply is gated behind a human approval that no agent holds the credential for — encoded, for example, in code/approval_gate.py, which refuses to let any destroy, public-read, or 0.0.0.0/0 plan through without a human typing an explicit acknowledgment and which appends every decision to an audit log. That audit log is not bureaucracy — when the EU AI Act’s logging duties bite (Article 12, covered in Ch. 14) and when the postmortem asks “who approved this,” the log is your answer. The servant’s every action is recorded; the master’s every approval is signed.


8.9 — Interactive Lab: IaC Validation Sandbox

Embedded directly below this chapter on the website is the IaC Validation Sandbox — the widget where this week’s central skill becomes muscle. It hands you an AI-generated artifact (an Ansible playbook and a Terraform snippet, the cousins of code/webserver.yml and code/storage.tf) with seeded mistakes: a hallucinated module or argument that does not exist, an insecure default (an open port, a public ACL), a plaintext secret, and a wrong ordering or non-idempotent step. Your job is to find and fix every one — exactly as you would gate a real PR from a coding agent.

Work it like a reviewer, not a reader. Go line by line. For each suspicious line, decide which failure class it is: does this module/argument actually exist (hallucination, LLM05)? Does this expose something it shouldn’t (insecure default)? Is there a secret in the file (plaintext credential)? Will this do harm if it runs twice (non-idempotency), or run in the wrong order? The Sandbox marks each mistake you catch and, just as importantly, flags the correct lines you wrongly accused — because a reviewer who cries wolf at every line is as useless as one who waves everything through.

What it teaches is the reflex the whole chapter is built to install: AI output is untrusted input until you have verified it. The Sandbox compresses a hundred real PR reviews into ten minutes of deliberate practice. Do it until finding the hallucinated module feels automatic — until your eyes snag on a too-plausible name the way a proofreader’s eye snags on a misspelling. That snag is the skill the master keeps. Run it twice more before the midterm; you will be doing exactly this, against the clock, in Project 8.


8.10 — The Steward and the Servant

Now the week’s question, head-on: what work is rightly given to the servant, and what must the master keep?

Scripture is not squeamish about servants and masters; it is precise about them. The faithful servant in the parables is entrusted with much, works diligently, and is commended — but the servant never becomes the owner. Luke 17:10 sets the posture exactly: “So you also, when you have done all that you were commanded, say, ‘We are unworthy servants; we have only done our duty.’” (ESV) The servant’s glory is in doing the duty well, not in bearing the authority. The authority — and the accountability that rides with it — stays with the master. That is not a demotion of the servant; it is the right ordering of the household.

Map that onto the agent and it stops being metaphor and becomes architecture. The AI servant can fetch (generate the playbook), propose (open the PR), labor tirelessly (validate, reformat, draft the runbook), and never grow weary. Give it that work freely — it is genuinely good at it, and refusing the help out of pride is its own failure of stewardship. But three things you, the steward, must keep, because they are not the servant’s to hold:

  • Judgmentshould this change happen, given a policy the model cannot know and a context it cannot weigh? The linter finds the broken; you find the wrong.
  • Verification — has someone read every line? The prudent gives thought to his steps (Prov. 14:15); the simple believes everything — and an unverified terraform apply is the technical definition of believing everything.
  • Accountability — when it breaks, who answers? Not the model. You. The audit log has your name in the approval, not the agent’s.

This is the LCMS doctrine of vocation doing real engineering work. You are placed in this office — administrator, steward of systems other people depend on — and the office carries a duty you cannot delegate to a tool, however capable. The two-kingdoms instinct sharpens it: the servant operates in the realm of means (it types, it builds, it executes); the master operates in the realm of ends and responsibility (what is built, why, and who bears the cost when it fails). Excessive Agency (§8.7) is, in this light, a theological error before it is a security one — it is handing the master’s keeping to the servant, giving away accountability you were never free to give. Least privilege is just stewardship with a config flag.

An unbelieving engineer should find the point just as sharp: a system where the actor and the accountable party are different entities, and the actor is fast, confident, and occasionally wrong, is a system optimized to ship mistakes at scale — unless the accountable party keeps the gate. The proverb is not piety bolted onto ops. It is the operating principle. Give thought to your steps. Read every line.


8.11 — Common Pitfalls

Pitfall: Trusting AI-generated config because it “looks right” and lints clean. Example: An agent’s playbook passes ansible-lint but opens SSH to 0.0.0.0/0 against your TLS-only, jump-host-only policy. Linters don’t know your policy. Fix: Run the full funnel (§8.6) and read the diff. Linters catch broken; a human catches wrong. Never apply a plan you haven’t read.


Pitfall: Accepting a hallucinated module, argument, or resource because the name is plausible. Example: community.general.ufw_rule (real module is ufw) or Terraform’s invented auto_tiering argument — both fluent, both nonexistent. Fix: ansible-lint / terraform validate flag unknown FQCNs and arguments. Make them a required CI gate; never merge a config that hasn’t passed them.


Pitfall: Writing non-idempotent tasks (bare shell:/command: with no creates:/changed_when:). Example: ansible.builtin.shell: ufw allow ... reports changed and re-runs its side effect on every single converge. Fix: Prefer the dedicated module (community.general.ufw); when you must shell out, guard it with creates:, changed_when:, or when:. Re-run and demand a clean ok second pass.


Pitfall: Granting the agent excessive agency — write access to main, broad cloud scopes, the apply credential. Example: An agent with apply rights ingests a poisoned issue (indirect injection) and provisions an attacker’s bucket. Fix: Least privilege (LLM06). Scope agents to isolated copilot/* branches, give them read-only roles for planning, hold the apply credential as a human. Apply Meta’s Rule of Two.


Pitfall: Committing secrets that the model helpfully inlined. Example: password: "P@ssw0rd123" in a playbook, now in git history forever — and AIs love to inline a placeholder that ships as-is. Fix: Secret scanning in CI (gitleaks, GitHub secret scanning) as a blocking gate; use Ansible Vault / a secrets manager / variables. Rotate anything that ever touched a repo.


Pitfall: No state management or no audit trail for applies. Example: Two engineers (or an agent and a human) terraform apply from different state, clobbering each other; later nobody can say who changed what. Fix: Remote, locked Terraform state; a human-gated apply step that logs every decision (code/approval_gate.py). The log answers “who approved this” before the postmortem has to.


Pitfall: Treating the dry-run as a formality. Example: Running terraform plan, seeing “3 to add, 1 to destroy,” and approving without reading which resource is being destroyed. Fix: Read the plan as the most important artifact in the pipeline. 1 to destroy on a production database is a different sentence than 1 to destroy on a temp bucket. The plan tells you what; you decide whether.


8.12 — Reps

Open the exercises for the full set. This week’s reps build the exact muscles the midterm demands: writing idempotent declarative tasks by hand, finding seeded mistakes in AI-generated IaC, and wiring the validate-and-gate funnel so that trust is cheap and skipping it is hard.

A preview:

  • Rep 1 — Convert an imperative bash setup into an idempotent Ansible playbook; prove it with a clean --check second run.
  • Rep 3 — Hunt the four seeded bugs in code/webserver.yml by hand, then confirm with ansible-lint. Predict before you lint.
  • Rep 6 — Run terraform validate/tflint on code/storage.tf, fix the public ACL, missing encryption, and hallucinated argument.
  • Rep 8 — Write a CI job that blocks a PR on a plaintext secret; test it with a deliberately planted secret.
  • Rep 10 — Wire code/approval_gate.py between plan and apply and confirm a destroy plan is held for a human.

AI policy for the reps: OFF — Phase 1. You may read about the agents; you may not use one to do the rep. You cannot gate an agent’s IaC later if you have never written idempotent IaC, or found a hallucinated module, with your own eyes and hands. Build the reflex first. A short “Check Your Reps” quiz is on this page — take it before you move on.


8.13 — This Week’s Project (THE MIDTERM)

Project 8 — “Generate, Verify, Trust” — is in Project 8, and it is the course midterm: a 60-minute, live, closed-AI, closed-internet, open-textbook build. No agent. No web. Just you, your editor, the standard toolchain, and this book. You will be handed AI-generated IaC riddled with the four failure classes, and you must verify it (find and name every defect), correct it (produce a clean, idempotent, secure version that lints and dry-runs cleanly), and trust it (defend, in writing, why the corrected version is now safe to apply) — under the clock.

The Normal tier proves you can find and fix the seeded defects and produce an idempotent, secure playbook + Terraform that pass the validation funnel. Medium adds the CI pipeline (validatelintpolicy/secret-scanplan) and the human-gated apply. Hard is the judgment deliverable an agent cannot write for you: a one-page memo deciding which of these tasks you would responsibly delegate to an AI agent going into Phase 2, which you would keep for a human gate, and why — mapped to LLM05/LLM06, the lethal trifecta, and the Rule of Two. The chapter also carries a cumulative midterm review of Weeks 1–7; the project file opens with it. Study it. The midterm assumes all seven weeks live in your hands, not just this one.


8.14 — Coach’s Final Word

This week the tool and the workload became one object, and you learned to hold it correctly. You learned that infrastructure becomes code — declarative, idempotent, version-controlled, with the file as the single source of truth — and that the same property that makes IaC reviewable (it’s just text) is what makes it a perfect target for an AI that generates fluent, confident, sometimes-wrong text. You learned the reference architecture in one sentence — AI proposes, CI validates, the human gates — and the three things the master keeps even when the servant does everything else: judgment, verification, accountability. You learned to name the dangers — Improper Output Handling, Excessive Agency, the lethal trifecta — and to design around them with least privilege, a deterministic validation funnel, a human-gated apply, and an audit log with your name on it.

If one habit survives this week, make it this: read every line before you apply. Not because the AI is bad — it is, genuinely, the most productive partner you will ever have at the keyboard — but because speed without verification is just a faster way to be wrong, and you are the one who answers for the result. The servant fetches and drafts and labors and never tires. The master gives thought to his steps.

Proverbs draws the line cleanly: the simple believes everything; the prudent gives thought to his steps. An AI agent in your pipeline is a powerful, willing, untiring servant — and a servant who believes everything, because it cannot do otherwise. Your vocation is to be the prudent one in the household: to receive the servant’s work gladly, to verify it faithfully, and to keep the accountability that was never the servant’s to hold. Do that, and AI is the best thing that ever happened to your change pipeline. Forget it, and you have built a fast machine for shipping mistakes.

Now go take the midterm. Prove you can do the work the servant does — so that in Phase 2 you have earned the right to direct it.

See you on Monday.


Up next: Read the exercises and complete every rep with AI off. Then open Project 8 — the midterm: Generate, Verify, Trust, in 60 closed-AI minutes, with a cumulative review of Weeks 1–7. Set up your toolchain via Appendix A; the local/cloud AI background is in Appendix B and the agentic-AI rules you’ll govern by in Phase 2 are in Appendix C. After the midterm, Chapter 9 — monitoring, observability, and keeping watch, where you learn to see what your now-coded infrastructure is doing.

Previously: Chapter 7 — containers and the sending out: packaging a model whole and shipping it into the world.

Interactive Lab — Week 8
IaC Validation Sandbox

An AI assistant generated this Ansible playbook. It looks plausible — but plausible is not correct. Click every line you believe is wrong, then press Validate. Read each line.

deploy_web.yml — generated by AI assistant
No lines flagged yet.
Try: flag a line you are sure is fine on purpose, then Validate — a false positive costs you too. Real review means defending every call, not just hunting for red flags.
Check Your Reps

Check Your Reps — Infrastructure as Code, and the Limits of the Servant

Question 1 of 5
What does it mean for a declarative IaC task to be idempotent?
Why: Idempotency means re-running converges to the same declared state, so a second run reports 'ok' rather than re-doing harm (§8.2).
Question 2 of 5
The chapter compresses the safe pattern for AI-generated IaC into one sentence. Which is it?
Why: The reference architecture is 'AI proposes, CI validates, the human gates' — three distinct trust levels, with the accountable human last (§8.4, §8.8).
Question 3 of 5
An AI-generated Ansible playbook references the module `community.general.ufw_rule`, which does not exist. What failure class is this, and what catches it?
Why: A confidently-emitted but nonexistent module name is the IaC face of LLM05 Improper Output Handling, and deterministic linting flags the unknown FQCN (§8.4).
Question 4 of 5
Per Meta's 'Agents Rule of Two,' when must a human be kept in the loop for an unsupervised IaC agent?
Why: The Rule of Two says an unsupervised agent should satisfy at most two of those three legs (the lethal trifecta); needing all three requires human-in-the-loop (§8.7).
Question 5 of 5
According to the chapter's apologetic, which three things must the human 'master' keep rather than delegate to the AI 'servant'?
Why: The servant fetches, drafts, and labors tirelessly, but judgment, verification, and accountability stay with the master — least privilege is stewardship with a config flag (§8.10).
YOU FINISHED. NICE WORK.