ЁЯПл The SchoolтА║ЁЯУЬ TerraformтА║ЁЯУЬ рдзрдбрд╛ 01 тАФ Infrastructure as code рдХрд╛: clicks рд╡рд┐рд░реБрджреНрдз рд▓рд┐рдЦрд┐рдд рдЖрд░рд╛рдЦрдбреЗ
ЁЯЦ╝я╕П See the drawing + lab ЁЯПа Course home ЁЯМ┐ Branch on GitHub тЬПя╕П View source
ЁЯЦ╝я╕П рдЖрдХреГрддреА рдЖрдгрд┐ labThe drawing + lab рдкреВрд░реНрдг рдкрд╛рдирд╛рд╡рд░ рдЙрдШрдбрд╛ тЖЧOpen full page тЖЧ

ЁЯУЬ рдзрдбрд╛ 01 тАФ Infrastructure as code рдХрд╛: clicks рд╡рд┐рд░реБрджреНрдз рд▓рд┐рдЦрд┐рдд рдЖрд░рд╛рдЦрдбреЗ

ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: рдзрдбрд╛ 01 / 12 ┬╖ рдкреБрдвреЗ: lesson-02-resources-providers


ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ

рд╕рдВрдкреВрд░реНрдг рдХреЛрд░реНрд╕ рдЬреНрдпрд╛ рдПрдХрд╛ рдХрд▓реНрдкрдиреЗрдмрджреНрджрд▓ рдЖрд╣реЗ рддреА: рд╢рд╛рд│реЗрдЪреЗ рдмрд╛рдВрдзрдХрд╛рдо рдХрд╛рд░реНрдпрд╛рд▓рдп рдЦреЛрд▓реНрдпрд╛, gates рдЖрдгрд┐ lockers рдмрд╛рдВрдзрддреЗ. рдЖрддрд╛рдкрд░реНрдпрдВрдд рдПрдХ clerk рддреЗ рд╣рд╛рддрд╛рдиреЗ рдмрд╛рдВрдзрдд рдЕрд╕реЗ, web console рдордзреНрдпреЗ click рдХрд░рдд. рдЖрдЬрдкрд╛рд╕реВрди рдХрд╛рд░реНрдпрд╛рд▓рдп рд▓рд┐рдЦрд┐рдд рдмрд╛рдВрдзрдХрд╛рдо рдЖрд░рд╛рдЦрдбреЗ рдареЗрд╡рддреЗ, рдЖрдгрд┐ рдПрдХ рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░ рддреНрдпрд╛рд╡рд░реВрди рдмрд╛рдВрдзрдХрд╛рдо рдХрд░рддреЛ. рдЦрд▒реНрдпрд╛ рдЖрдпреБрд╖реНрдпрд╛рдд рдЖрд░рд╛рдЦрдбрд╛ рдореНрд╣рдгрдЬреЗ Terraform (рдХрд┐рдВрд╡рд╛ OpenTofu) files, рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░ рдореНрд╣рдгрдЬреЗ terraform program, рдЖрдгрд┐ campus рдореНрд╣рдгрдЬреЗ рддреБрдордЪреЗ cloud account. рд╕рдВрдкреВрд░реНрдг рдХреЛрд░реНрд╕рднрд░ рддреБрдореНрд╣реА рд╡рд╛рдкрд░рд╛рд▓ рддреНрдпрд╛ рдЦрд▒реНрдпрд╛ files:

ЁЯОТ рд╕реБрд░реВ рдХрд░рдгреНрдпрд╛рдЖрдзреА: рддреБрдореНрд╣рд╛рд▓рд╛ рдлрдХреНрдд Python 3 рд▓рд╛рдЧреЗрд▓, рдмрд╛рдХреА рдХрд╛рд╣реА рдирд╛рд╣реА тАФ cloud account рдирдХреЛ, pip install рдирдХреЛ, terraform binary рдирдХреЛ. iac/ folder рд╣реЗ Terraform рдХрд╕реЗ рдХрд╛рдо рдХрд░рддреЗ рдпрд╛рдЪреЗ рд╢рд┐рдХрд╡рдгреНрдпрд╛рд╕рд╛рдареАрдЪреЗ model рдЖрд╣реЗ, рд╕реНрд╡рддрдГ Terraform рдирд╛рд╣реА. Randomness seeded рдЖрд╣реЗ, рдореНрд╣рдгреВрди рдкреНрд░рддреНрдпреЗрдХ run рддреАрдЪ рдЧреЛрд╖реНрдЯ print рдХрд░рддреЛ. рдЦрд▒реНрдпрд╛ account рд╡рд░ рдЕрд╕реЗ рд▓рд┐рд╣рд┐рд▓реЗрд▓реНрдпрд╛ commands рд╕рд╛рдареА Terraform (1.5 рдХрд┐рдВрд╡рд╛ рдирд╡реАрди) рдЖрдгрд┐ рдЦрд░реЗ cloud account рд▓рд╛рдЧрддреЗ, рдЖрдгрд┐ рддреНрдпрд╛рдВрдЪрд╛ рдЦрд░реНрдЪ рдпреЗрдК рд╢рдХрддреЛ.

ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ

рд╢рд╛рд│реЗрд▓рд╛ рддреАрди рдЧрд╛рд╡рд╛рдВрдд рдПрдХ рдирд╡реАрди science wing рд╣рд╡реА рдЖрд╣реЗ: рдПрдХ chemistry lab, рдПрдХ рдореБрдЦреНрдп gate рдЖрдгрд┐ рддреАрди lockers. рддреАрдЪ wing, рддреАрди рд╡реЗрд│рд╛.

рдХрддрд░рд┐рдирд╛, clerk, рдкреНрд░рддреНрдпреЗрдХ wing рд╣рд╛рддрд╛рдиреЗ рдмрд╛рдВрдзрддреЗ. рддреА рдкреНрд░рддреНрдпреЗрдХ рддрдкрд╢реАрд▓ рдПрдХрд╛ form рдордзреНрдпреЗ рдЯрд╛рдЗрдк рдХрд░рддреЗ: рдЦреЛрд▓реАрдЪреЗ рдирд╛рд╡, floor, seats рдЪреА рд╕рдВрдЦреНрдпрд╛, gate рдЙрдШрдбрдгреНрдпрд╛рдЪреНрдпрд╛ рд╡реЗрд│рд╛. рдкреНрд░рддреНрдпреЗрдХ site рд▓рд╛ рдЖрда fields. рддреА рдХрд╛рд│рдЬреАрдкреВрд░реНрд╡рдХ рдХрд╛рдо рдХрд░рддреЗ. рдкрдг рддрд┐рд╕рд▒реНрдпрд╛ site рд╡рд░ рддреА floor 2 рдРрд╡рдЬреА floor 20 рдЯрд╛рдЗрдк рдХрд░рддреЗ. рдХреЛрдгрд╛рдЪреНрдпрд╛рдЪ рд▓рдХреНрд╖рд╛рдд рдпреЗрдд рдирд╛рд╣реА. рдЖрддрд╛ рддрд┐рдиреНрд╣реА wings рд╕рд╛рд░рдЦреНрдпрд╛ рдирд╛рд╣реАрдд, рдЖрдгрд┐ рдХрд╛ рддреЗ рдХреЛрдгреАрдЪ рд▓рд┐рд╣реВрди рдареЗрд╡рд▓реЗрд▓реЗ рдирд╛рд╣реА.

рдордЧ рджреАрдкрд┐рдХрд╛рд▓рд╛ рдПрдХ рдХрд▓реНрдкрдирд╛ рд╕реБрдЪрддреЗ. "рдЖрдкрдг рдЖрд░рд╛рдЦрдбрд╛ рдПрдХрджрд╛рдЪ рд▓рд┐рд╣реВрди рдареЗрд╡реВ. ЁЯУЬ рдордЧ рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░ рдЖрд░рд╛рдЦрдбреНрдпрд╛рд╡рд░реВрди, рдЬрд╕рд╛ рд▓рд┐рд╣рд┐рд▓рд╛ рдЖрд╣реЗ рддрд╕рд╛рдЪ, рджрд░ рд╡реЗрд│реА рдмрд╛рдВрдзреЗрд▓."

рдЖрддрд╛ рддрд┐рдиреНрд╣реА wings рд╕рд╛рд░рдЦреНрдпрд╛ рдЖрд╣реЗрдд. рдЖрдгрд┐ рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░ рддреНрдпрд╛рдЪ рдЖрд░рд╛рдЦрдбреНрдпрд╛рд╕рд╣ рдкреВрд░реНрдг рдЭрд╛рд▓реЗрд▓реНрдпрд╛ wing рд▓рд╛ рдкреБрдиреНрд╣рд╛ рднреЗрдЯ рджреЗрддреЛ рддреЗрд╡реНрд╣рд╛ рддреЛ рдореНрд╣рдгрддреЛ: "рдХрд╛рд╣реАрдЪ рдХрд░рд╛рдпрдЪреЗ рдирд╛рд╣реА. Wing рдЖрдзреАрдЪ рдЖрд░рд╛рдЦрдбреНрдпрд╛рд╢реА рдЬреБрд│рддреЗ." ЁЯС╖

ЁЯЧ║я╕П рдЖрдХреГрддреА

flowchart LR
    subgraph hand["ЁЯЦ▒я╕П by hand"]
      k["Katrina types 8 fields<br/>per site"] --> d1["dev тЬЕ"]
      k --> s1["staging тЬЕ"]
      k --> p1["prod тЭМ floor 20"]
    end
    subgraph plans["ЁЯУЬ written plans"]
      f["one config file<br/>(in git)"] --> c["ЁЯС╖ the contractor<br/>(terraform apply)"]
      c --> d2["dev тЬЕ"]
      c --> s2["staging тЬЕ"]
      c --> p2["prod тЬЕ"]
      c -.->|"run again"| n["No changes."]
    end

ЁЯЧ║я╕П рд░реЗрдЦрд╛рдЯрд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/terraform/lesson-diagrams.html#l01

тЭУ рдХрд╛рдп

ЁЯдФ рдХрд╛

рдХрд╛рд░рдг рд╣рд╛рддрд╛рдиреЗ рдмрд╛рдВрдзрд▓реЗрд▓рд╛ campus рдХреЛрдгреАрдЪ рдкреБрдиреНрд╣рд╛ рдмрд╛рдВрдзреВ рд╢рдХрдд рдирд╛рд╣реА. рд░рд╛рддреНрд░реА 2 рд╡рд╛рдЬрддрд╛ prod рдмрд┐рдШрдбрд▓реЗ рдХреА рдкреНрд░рд╢реНрди рдЕрд╕рддреЛ "рдХрд╛рдп рдмрджрд▓рд▓реЗ?" Clicks рдЕрд╕рддреАрд▓ рддрд░ рдЙрддреНрддрд░ рдХреЛрдгрд╛рдЪреНрдпрд╛ рддрд░реА рдЖрдард╡рдгреАрдд рдЕрд╕рддреЗ. Git рдордзреАрд▓ рдЖрд░рд╛рдЦрдбреНрдпрд╛рд╕рд╣ рдЙрддреНрддрд░ рдореНрд╣рдгрдЬреЗ author, рддрд╛рд░реАрдЦ рдЖрдгрд┐ review рдЕрд╕рд▓реЗрд▓рд╛ рдПрдХ commit. рдЖрд░рд╛рдЦрдбреНрдпрд╛рдореБрд│реЗ dev, staging рдЖрдгрд┐ prod рдордзреНрдпреЗ рддреАрдЪ рдЧреЛрд╖реНрдЯ рдмрд╛рдВрдзрддрд╛ рдпреЗрддреЗ, рдореНрд╣рдгреВрди staging рдордзреАрд▓ test рд▓рд╛ рдХрд╛рд╣реАрддрд░реА рдЕрд░реНрде рдЕрд╕рддреЛ. рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ click рдорд╛рдЧрдЪреА рдЫреЛрдЯреАрд╢реА рдЪреВрдХ рдкрдЯрдХрди рд╡рд╛рдврдд рдЬрд╛рддреЗ: рдЦрд╛рд▓рдЪрд╛ рдХрд░реВрди-рдкрд╛рд╣рд╛ рджрд╛рдЦрд╡рддреЛ рдХреА рдкреНрд░рддреНрдпреЗрдХ field рд▓рд╛ рдлрдХреНрдд 2% рдЪреВрдХ рдЕрд╕рд▓реА рддрд░реА рджрд╣рд╛рдкреИрдХреА рдПрдХ site рдмрд┐рдШрдбрддреЗ.

ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)

iac/cloud.py рдордзреАрд▓ by_hand(cloud, rng, slip) рдореНрд╣рдгрдЬреЗ console рд╡рд░рдЪреА рдХрддрд░рд┐рдирд╛: рдкреНрд░рддреНрдпреЗрдХ site рд▓рд╛ 8 fields, рдЖрдгрд┐ рдкреНрд░рддреНрдпреЗрдХ field рдордзреНрдпреЗ рдЪреВрдХ рд╣реЛрдгреНрдпрд╛рдЪреА slip рд╢рдХреНрдпрддрд╛ (рдПрдЦрд╛рджрд╛ рдЖрдХрдбрд╛ ├Ч10 рдЯрд╛рдЗрдк рд╣реЛрдгреЗ, рджреЛрди рдЕрдХреНрд╖рд░рд╛рдВрдЪреА рдЕрджрд▓рд╛рдмрджрд▓, рдПрдЦрд╛рджрд╛ locker рд╡рд┐рд╕рд░рдгреЗ). fingerprint(cloud) campus рдиреЗ рдирд┐рд╡рдбрд▓реЗрд▓реНрдпрд╛ ids рд╢рд┐рд╡рд╛рдп site рдЪреЗ рд╡рд░реНрдгрди рдХрд░рддреЗ, рдореНрд╣рдгреВрди рд╕рд╛рд░рдЦреНрдпрд╛ рдкрджреНрдзрддреАрдиреЗ рдмрд╛рдВрдзрд▓реЗрд▓реНрдпрд╛ рджреЛрди sites рдЬреБрд│рддрд╛рдд. iac/demo.py рдордзреАрд▓ why() рддреАрди sites рд╣рд╛рддрд╛рдиреЗ рдмрд╛рдВрдзрддреЗ, рдордЧ рддреАрди рдЖрд░рд╛рдЦрдбреНрдпрд╛рд╡рд░реВрди (CAMPUS, рдПрдХ config dict) iac/engine.py рдордзреАрд▓ plan() рдЖрдгрд┐ apply() рд╡рд╛рдкрд░реВрди рдмрд╛рдВрдзрддреЗ, рдЖрдгрд┐ рдордЧ рдкреВрд░реНрдг рдЭрд╛рд▓реЗрд▓реНрдпрд╛ site рд╡рд┐рд░реБрджреНрдз рдЖрдгрдЦреА рдПрдХрджрд╛ plan рдХрд░рддреЗ.

ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛

python3 iac/demo.py why
python3 - <<'EOF'
import sys; sys.path.insert(0, "iac")
from cloud import FakeCloud, Rng, by_hand, fingerprint
from engine import plan, apply
from demo import CAMPUS
good = FakeCloud(); apply(plan(CAMPUS, None, good), good); want = fingerprint(good)
for slip in (0.0, 0.02, 0.05, 0.1, 0.2):
    rng, ok = Rng(5), 0
    for site in range(10):
        c = FakeCloud(); by_hand(c, rng, slip); ok += fingerprint(c) == want
    print(f"slip {slip:>4.0%} per field тЖТ {ok:>2} of 10 hand-built sites match the plans")
EOF
python3 iac/test_iac.py

тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ

why рдЕрд╕реЗ print рдХрд░рддреЗ:

тФАтФА the works office builds the same campus 3 times BY HAND (8 fields per site, 10% chance of a slip per field)
   dev      8 fields typed ┬╖ 5 objects ┬╖ slips: none
   staging  8 fields typed ┬╖ 5 objects ┬╖ slips: none
   prod     8 fields typed ┬╖ 5 objects ┬╖ slips: floor typed 20 instead of 2
   do the 3 hand-built sites match? False
   do the 3 plan-built sites match? True
тФАтФА apply the same plans to the dev site again тЖТ No changes. Your infrastructure matches the configuration.

рддреБрдордЪрд╛ snippet рдЕрд╕реЗ print рдХрд░рддреЛ:

slip   0% per field тЖТ 10 of 10 hand-built sites match the plans
slip   2% per field тЖТ  9 of 10 hand-built sites match the plans
slip   5% per field тЖТ  7 of 10 hand-built sites match the plans
slip  10% per field тЖТ  5 of 10 hand-built sites match the plans
slip  20% per field тЖТ  2 of 10 hand-built sites match the plans

Tests рдЪреНрдпрд╛ рд╢реЗрд╡рдЯреА 12/12 passed рдпреЗрддреЗ.

ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ

рдЖрд░рд╛рдЦрдбреНрдпрд╛рдиреЗ рддреАрди рдПрдХрд╕рд╛рд░рдЦреНрдпрд╛ sites рдмрд╛рдВрдзрд▓реНрдпрд╛, рдЖрдгрд┐ рджреБрд╕рд▒реНрдпрд╛ run рдиреЗ рдХрд╛рд╣реАрдЪ рдмрджрд▓рд▓реЗ рдирд╛рд╣реА тАФ рдЖрд░рд╛рдЦрдбрд╛ рдкреБрдиреНрд╣рд╛-рдкреБрдиреНрд╣рд╛ рд╕рд╛рд░рдЦрд╛рдЪ (repeatable) рдЖрдгрд┐ idempotent рдЖрд╣реЗ. рд╣рд╛рддрд╛рдиреЗ рдмрд╛рдВрдзрддрд╛рдирд╛ рдирд┐рдХрд╛рд▓ рдирд╢рд┐рдмрд╛рд╡рд░ рдЕрд╡рд▓рдВрдмреВрди рдЕрд╕рддреЛ: 10% slip rate рд▓рд╛ 10 рдкреИрдХреА рдлрдХреНрдд 5 sites рдмрд░реЛрдмрд░ рдЖрд▓реНрдпрд╛, рдЖрдгрд┐ 2% рд▓рд╛ рд╕реБрджреНрдзрд╛ рджрд╣рд╛рдкреИрдХреА рдПрдХ site рдЪреБрдХреАрдЪреА рд╣реЛрддреА. (рд╣реЗ slip rates рдХрд╛рд▓реНрдкрдирд┐рдХ рдЖрд╣реЗрдд; рдореБрджреНрджрд╛ рд╣рд╛ рдЖрд╣реЗ рдХреА рдкреНрд░рддреНрдпреЗрдХ click рдордзреАрд▓ рдЫреЛрдЯреНрдпрд╛ рдЪреБрдХрд╛ рдЕрдиреЗрдХ fields рдЖрдгрд┐ рдЕрдиреЗрдХ sites рдордзреНрдпреЗ рдЧреБрдгрд╛рдХрд╛рд░рд╛рдиреЗ рд╡рд╛рдврддрд╛рдд.)

тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд

рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ рд╕рд░реНрд╡рд╛рдд рдЫреЛрдЯрд╛ рдЦрд░рд╛ Terraform workflow. Terraform (рдХрд┐рдВрд╡рд╛ OpenTofu) install рдХрд░рд╛, рдПрдХ main.tf рд▓рд┐рд╣рд╛, рдЖрдгрд┐ рдордЧ:

terraform init      # download the providers named in the config (lesson 02)
terraform plan      # show what would change тАФ nothing is built yet (lesson 03)
terraform apply     # show the plan again, ask "yes", then build it
terraform apply     # run it again: "No changes. Your infrastructure matches the configuration."

OpenTofu рдордзреНрдпреЗ commands рддрд╢рд╛рдЪ рдЖрд╣реЗрдд, рдлрдХреНрдд terraform рдРрд╡рдЬреА tofu. рддреБрдореНрд╣рд╛рд▓рд╛ рднреЗрдЯрдгрд╛рд░реА рдЗрддрд░ IaC tools:

ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: рдЖрдЬ рд╣рд╛рддрд╛рдиреЗ рдмрд╛рдВрдзрд▓реЗрд▓рд╛ infrastructure рдЪрд╛ рдПрдХ рддреБрдХрдбрд╛ рдирд┐рд╡рдбрд╛ (рдПрдХ bucket, рдПрдХ DNS zone, рдПрдХ рдЫреЛрдЯреА service). рддреЛ code рдореНрд╣рдгреВрди рд▓рд┐рд╣рд╛, рдЕрд╕реНрддрд┐рддреНрд╡рд╛рдд рдЕрд╕рд▓реЗрд▓реЗ objects import рдХрд░рд╛ (рдзрдбрд╛ 09), рдЖрдгрд┐ рддреНрдпрд╛ рддреБрдХрдбреНрдпрд╛рд╕рд╛рдареА "console рдордзреНрдпреЗ рдмрджрд▓ рдирд╛рд╣реАрдд" рд╣рд╛ рдирд┐рдпрдо рдХрд░рд╛.

тПня╕П рдкреБрдвреЗ

рдЖрд░рд╛рдЦрдбреНрдпрд╛рд▓рд╛ рдПрдХ рд╢рдмреНрджрд╕рдВрдЧреНрд░рд╣ рд▓рд╛рдЧрддреЛ: resource рдореНрд╣рдгрдЬреЗ рдХрд╛рдп, provider рдХреЛрдг, рдЖрдгрд┐ рдЖрд░рд╛рдЦрдбреНрдпрд╛рдд рдХреЛрдгрддреА fields рдард░рд╡рддрд╛ рдпреЗрддрд╛рдд?

git checkout lesson-02-resources-providers

ЁЯУЬ Lesson 01 тАФ Why infrastructure as code: clicks vs written plans

ЁЯУН You are here: Lesson 01 of 12 ┬╖ Next: lesson-02-resources-providers


ЁЯУж What's in this branch

The one idea this whole course is about: the school's works office builds rooms, gates and lockers. Until now, a clerk built them by hand, clicking through a web console. From today the office keeps written building plans, and a contractor builds from them. In real life the plans are Terraform (or OpenTofu) files, the contractor is the terraform program, and the campus is your cloud account. Real files you will use all the way through:

ЁЯОТ Before you start: you need Python 3 and nothing else тАФ no cloud account, no pip install, no terraform binary. The iac/ folder is a teaching model of how Terraform works, not Terraform itself. Randomness is seeded, so every run prints the same story. Commands marked on a real account need Terraform (1.5 or newer) and a real cloud account, and they can cost money.

ЁЯзТ Explain like I'm 5

The school needs a new science wing in three towns: a chemistry lab, a main gate and three lockers. Same wing, three times.

Katrina, the clerk, builds each one by hand. She types every detail into a form: the room name, the floor, the number of seats, the gate's opening hours. Eight fields per site. She is careful. But on the third site she types floor 20 instead of floor 2. Nobody notices. Now the three wings are not the same, and nobody wrote down why.

Then Dipika has an idea. "Let's write the plans down once. ЁЯУЬ Then a contractor builds from the plans, exactly as written, every time."

Now the three wings are the same. And when the contractor visits a finished wing again with the same plans, it says: "Nothing to do. The wing already matches the plans." ЁЯС╖

ЁЯЧ║я╕П Diagram

flowchart LR
    subgraph hand["ЁЯЦ▒я╕П by hand"]
      k["Katrina types 8 fields<br/>per site"] --> d1["dev тЬЕ"]
      k --> s1["staging тЬЕ"]
      k --> p1["prod тЭМ floor 20"]
    end
    subgraph plans["ЁЯУЬ written plans"]
      f["one config file<br/>(in git)"] --> c["ЁЯС╖ the contractor<br/>(terraform apply)"]
      c --> d2["dev тЬЕ"]
      c --> s2["staging тЬЕ"]
      c --> p2["prod тЬЕ"]
      c -.->|"run again"| n["No changes."]
    end

ЁЯЧ║я╕П Drawn version + a lab: https://school-edh.pages.dev/terraform/lesson-diagrams.html#l01

тЭУ What

ЁЯдФ Why

Because a campus built by hand is a campus nobody can rebuild. When prod breaks at 2 a.m., the question is "what changed?" With clicks, the answer is in someone's memory. With plans in git, the answer is a commit with an author, a date and a review. Plans also let you build the same thing in dev, staging and prod, so a test in staging means something. And a small slip rate per click adds up fast: the Try-it below shows that even 2% per field breaks one site in ten.

ЁЯФз How (in this repo)

by_hand(cloud, rng, slip) in iac/cloud.py is Katrina at the console: 8 fields per site, and each field has a slip chance of a mistake (a number typed ├Ч10, two characters swapped, a locker forgotten). fingerprint(cloud) describes a site without the ids the campus chose, so two sites built the same way match. why() in iac/demo.py builds three sites by hand, then three from the plans (CAMPUS, a config dict) with plan() and apply() from iac/engine.py, and then plans once more against a finished site.

ЁЯзк Try it

python3 iac/demo.py why
python3 - <<'EOF'
import sys; sys.path.insert(0, "iac")
from cloud import FakeCloud, Rng, by_hand, fingerprint
from engine import plan, apply
from demo import CAMPUS
good = FakeCloud(); apply(plan(CAMPUS, None, good), good); want = fingerprint(good)
for slip in (0.0, 0.02, 0.05, 0.1, 0.2):
    rng, ok = Rng(5), 0
    for site in range(10):
        c = FakeCloud(); by_hand(c, rng, slip); ok += fingerprint(c) == want
    print(f"slip {slip:>4.0%} per field тЖТ {ok:>2} of 10 hand-built sites match the plans")
EOF
python3 iac/test_iac.py

тЬЕ Verify тАФ what you should see

why prints:

тФАтФА the works office builds the same campus 3 times BY HAND (8 fields per site, 10% chance of a slip per field)
   dev      8 fields typed ┬╖ 5 objects ┬╖ slips: none
   staging  8 fields typed ┬╖ 5 objects ┬╖ slips: none
   prod     8 fields typed ┬╖ 5 objects ┬╖ slips: floor typed 20 instead of 2
   do the 3 hand-built sites match? False
   do the 3 plan-built sites match? True
тФАтФА apply the same plans to the dev site again тЖТ No changes. Your infrastructure matches the configuration.

Your snippet prints:

slip   0% per field тЖТ 10 of 10 hand-built sites match the plans
slip   2% per field тЖТ  9 of 10 hand-built sites match the plans
slip   5% per field тЖТ  7 of 10 hand-built sites match the plans
slip  10% per field тЖТ  5 of 10 hand-built sites match the plans
slip  20% per field тЖТ  2 of 10 hand-built sites match the plans

The tests end with 12/12 passed.

ЁЯПБ What you just proved

The plans built three identical sites, and a second run changed nothing тАФ the plans are repeatable and idempotent. By hand, the result depends on luck: at a 10% slip rate only 5 of 10 sites came out right, and even at 2% one site in ten was wrong. (The slip rates are made up; the point is that small per-click errors multiply over many fields and many sites.)

тЪая╕П Common mistakes

ЁЯПн In production

On a real account тАФ the smallest real Terraform workflow. Install Terraform (or OpenTofu), write a main.tf, then:

terraform init      # download the providers named in the config (lesson 02)
terraform plan      # show what would change тАФ nothing is built yet (lesson 03)
terraform apply     # show the plan again, ask "yes", then build it
terraform apply     # run it again: "No changes. Your infrastructure matches the configuration."

With OpenTofu the commands are the same, with tofu instead of terraform. Other IaC tools you will meet:

ЁЯПн Why this matters in production: pick one piece of infrastructure that is built by hand today (a bucket, a DNS zone, a small service). Write it as code, import the existing objects (lesson 09), and make "no console changes" the rule for that piece.

тПня╕П Next

Plans need a vocabulary: what is a resource, who is the provider, and which fields may the plans set?

git checkout lesson-02-resources-providers
тЖР Course homeall lessonsNext тЖТresources providers

This page is the lesson's README from the lesson-01-why-iac branch, shown here so the whole School stays on one site. Code files open on GitHub at the same branch.