ЁЯФБ рдзрдбрд╛ 04 тАФ Variables, outputs рдЖрдгрд┐ loops: рдПрдХ рдЖрд░рд╛рдЦрдбрд╛, рдЕрдиреЗрдХ рдЦреЛрд▓реНрдпрд╛
ЁЯУН рддреБрдореНрд╣реА рдЗрдереЗ рдЖрд╣рд╛рдд: рдзрдбрд╛ 04 / 12 ┬╖ рдорд╛рдЧреЗ: lesson-03-plan-apply ┬╖ рдкреБрдвреЗ: lesson-05-state
ЁЯУж рдпрд╛ рдмреНрд░рдБрдЪрдордзреНрдпреЗ рдХрд╛рдп рдЖрд╣реЗ
рдзрдбреЗ 01тАУ03, рдЖрдгрд┐ рдЖрд░рд╛рдЦрдбрд╛ рдкреБрдиреНрд╣рд╛ рд╡рд╛рдкрд░рддрд╛ рдпреЗрдгреНрдпрд╛рдЬреЛрдЧрд╛ рдХрд░рдгрд╛рд░реЗ рднрд╛рд╖реЗрдЪреЗ рднрд╛рдЧ:
input variables (рддреБрдореНрд╣реА рднрд░рддрд╛ рддреНрдпрд╛ рд░рд┐рдХрд╛рдореНрдпрд╛ рдЬрд╛рдЧрд╛), locals (рдПрдХрджрд╛ рдореЛрдЬрд▓реЗрд▓реНрдпрд╛ рдореВрд▓реНрдпрд╛рдВрд╕рд╛рдареА рдирд╛рд╡реЗ),
outputs (рдЖрд░рд╛рдЦрдбрд╛ рдкрд░рдд рдХрд╛рдп рд╕рд╛рдВрдЧрддреЛ) рдЖрдгрд┐ рджреЛрди loops, count рдЖрдгрд┐
for_each. рдореБрдЦреНрдп рдзрдбрд╛: count рдЖрдкрд▓реНрдпрд╛ рдкреНрд░рддреАрдВрдирд╛ рдЬрд╛рдЧреЗрдиреБрд╕рд╛рд░ рдирдВрдмрд░ рджреЗрддреЛ, for_each
рдирд╛рд╡рд╛рдиреЗ тАФ рдЖрдгрд┐ рдПрдЦрд╛рджреА рдкреНрд░рдд рдХрд╛рдврд▓реНрдпрд╛рд╡рд░ рдХрд╛рдп рд╣реЛрддреЗ рддреЗ рдпрд╛рд╡рд░реВрдирдЪ рдард░рддреЗ. iac/demo.py рдордзреАрд▓
variables(); iac/engine.py рдордзреАрд▓ expand() рдЖрдгрд┐ resolve().
ЁЯзТ 5 рд╡рд░реНрд╖рд╛рдВрдЪреНрдпрд╛ рдореБрд▓рд╛рд▓рд╛ рд╕рдордЬрд╛рд╡рд▓реНрдпрд╛рд╕рд╛рд░рдЦреЗ
Lab рдЪреНрдпрд╛ рдЖрд░рд╛рдЦрдбреНрдпрд╛рдд рдЖрддрд╛ рд░рд┐рдХрд╛рдореНрдпрд╛ рдЬрд╛рдЧрд╛ рдЖрд╣реЗрдд: "town: ____ ┬╖ seats: ____". ЁЯЦКя╕П рддреНрдпрд╛ "dev, 30" рдиреЗ рднрд░рд╛ рдЖрдгрд┐ рддреБрдореНрд╣рд╛рд▓рд╛ рдЫреЛрдЯреА lab рдорд┐рд│рддреЗ. "prod, 40" рдиреЗ рднрд░рд╛ рдЖрдгрд┐ рдореЛрдареА рдорд┐рд│рддреЗ. рдЖрд░рд╛рдЦрдбрд╛ рддреЛрдЪ.
Lab рд▓рд╛ lockers a, b рдЖрдгрд┐ c рд╕реБрджреНрдзрд╛ рд╣рд╡реЗ рдЖрд╣реЗрдд. рд╣реЗ рд▓рд┐рд╣реВрди рдареЗрд╡рдгреНрдпрд╛рдЪреЗ рджреЛрди рдорд╛рд░реНрдЧ рдЖрд╣реЗрдд.
рдорд╛рд░реНрдЧ 1 тАФ рдирдВрдмрд░ (count). "Locker #0 рдореНрд╣рдгрдЬреЗ a, #1 рдореНрд╣рдгрдЬреЗ b, #2 рдореНрд╣рдгрдЬреЗ c." рдордЧ locker b рдЪреА
рдЧрд░рдЬ рд╕рдВрдкрддреЗ. рдЖрддрд╛ рдпрд╛рджреА "a, c" рдЖрд╣реЗ. рдореНрд╣рдгреВрди #0 рдореНрд╣рдгрдЬреЗ a, рдЖрдгрд┐ #1 рдЖрддрд╛ c рдЖрд╣реЗ. рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░
рд╡рд┐рдЪрд╛рд░ рдХрд░рддреЛ: "#1 рд╡рд░ рдЖрдзреА b рд▓рд┐рд╣рд┐рд▓реЗ рд╣реЛрддреЗ, рдЖрддрд╛ c рдЖрд╣реЗ тАФ рд▓реЗрдмрд▓ рдмрджрд▓рд╛. рдЖрдгрд┐ #2 рдЖрддрд╛ рдпрд╛рджреАрдд рдирд╛рд╣реА тАФ
рддреЛ рдХрд╛рдвреВрди рдЯрд╛рдХрд╛." рдкрдг #2 рд╣рд╛ рдЦрд░рд╛ locker c рд╣реЛрддрд╛, рдЖрдд рдХреЛрдгрд╛рдЪреА рддрд░реА рдкреБрд╕реНрддрдХреЗ рдЕрд╕рд▓реЗрд▓рд╛! ЁЯУЪЁЯШ▒
рдорд╛рд░реНрдЧ 2 тАФ рдирд╛рд╡реЗ (for_each). "a рдирд╛рд╡рд╛рдЪрд╛ locker, b рдирд╛рд╡рд╛рдЪрд╛ locker, c рдирд╛рд╡рд╛рдЪрд╛ locker."
b рдХрд╛рдврд╛, рдЖрдгрд┐ рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░ рдлрдХреНрдд locker b рдХрд╛рдврддреЛ. рдмрд╛рдХреА рдХрд╛рд╣реАрдЪ рд╣рд▓рдд рдирд╛рд╣реА.
ЁЯЧ║я╕П рдЖрдХреГрддреА
flowchart LR
subgraph cnt["count = length(var.labels) тАФ by POSITION"]
c0["row[0] a"] --> c0b["row[0] a тЬУ"]
c1["row[1] b"] --> c1b["row[1] c ~ relabel"]
c2["row[2] c"] --> c2b["row[2] - destroy"]
end
subgraph fe["for_each = var.labels тАФ by NAME"]
fa["row["a"]"] --> fa2["тЬУ"]
fb["row["b"]"] --> fb2["- destroy"]
fc["row["c"]"] --> fc2["тЬУ"]
end
ЁЯЧ║я╕П рд░реЗрдЦрд╛рдЯрд▓реЗрд▓реА рдЖрд╡реГрддреНрддреА + рдПрдХ lab: https://school-edh.pages.dev/terraform/lesson-diagrams.html#l04
тЭУ рдХрд╛рдп
- Input variable тАФ
variable "seats" { default = 30 },var.seatsрдореНрд╣рдгреВрди рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рддреЗ. рддреЗ-var seats=40,.tfvarsfile (-var-file=prod.tfvars), рдХрд┐рдВрд╡рд╛ environment variableTF_VAR_seats=40рдиреЗ рдард░рд╡рд╛. Default рдирд╕рд▓реЗрд▓реЗ variable рджреЗрдгреЗ рдЖрд╡рд╢реНрдпрдХрдЪ рдЖрд╣реЗ.validationblock рдЪреБрдХреАрдЪреА рдореВрд▓реНрдпреЗ рд▓рд╡рдХрд░рдЪ рдирд╛рдХрд╛рд░реВ рд╢рдХрддреЛ. - Local value тАФ
locals { prefix = "${var.env}-school" },local.prefixрдореНрд╣рдгреВрди рд╡рд╛рдкрд░рд▓реЗ рдЬрд╛рддреЗ. рдПрдХрд╛ expression рд╕рд╛рдареА рдирд╛рд╡, рдореНрд╣рдгрдЬреЗ рддреЗ рдПрдХрджрд╛рдЪ рд▓рд┐рд╣рд╛рд╡реЗ рд▓рд╛рдЧрддреЗ. - Output тАФ
output "lab_name" { value = school_room.lab.name }. Apply рдирдВрддрд░ print рд╣реЛрддреЗ,terraform outputрдиреЗ рд╡рд╛рдЪрддрд╛ рдпреЗрддреЗ, рдЖрдгрд┐ module рдореВрд▓реНрдпреЗ рдкрд░рдд рджреЗрддреЛ рддреЛ рдорд╛рд░реНрдЧ рд╣рд╛рдЪ (рдзрдбрд╛ 07).sensitive = trueрддреЗ screen рд╡рд░ рд▓рдкрд╡рддреЗ (рддреЗ рдХрд╛рдп рд▓рдкрд╡рдд рдирд╛рд╣реА рддреЗ рдзрдбрд╛ 05 рджрд╛рдЦрд╡рддреЛ). count = NтАФ N рдкреНрд░рддреА,row[0],row[1], тАж рдЕрд╕реЗ address.count.indexрд╣рд╛ рдирдВрдмрд░.for_each = map рдХрд┐рдВрд╡рд╛ setтАФ рдкреНрд░рддреНрдпреЗрдХ key рд╕рд╛рдареА рдПрдХ рдкреНрд░рдд,row["a"]рдЕрд╕рд╛ address. рдЖрддeach.keyрдЖрдгрд┐each.value. List рдЖрдзреА set рдордзреНрдпреЗ рдмрджрд▓рд╛рд╡реА рд▓рд╛рдЧрддреЗ:for_each = toset(var.labels).- Splat тАФ
school_locker.row[*].idтАФ рд╕рд░реНрд╡ рдкреНрд░рддреАрдВрдЪреНрдпрд╛ ids рдЪреА list. - Expression тАФ HCL рдордзреНрдпреЗ functions (
length,lookup,merge,cidrsubnet, тАж), conditionals (a ? b : c) рдЖрдгрд┐forexpressions рдЕрд╕рддрд╛рдд. Lab рдордзреНрдпреЗ рдПрдХ function рдЖрд╣реЗ,length().
ЁЯдФ рдХрд╛
рдХрд╛рд░рдг variables рд╢рд┐рд╡рд╛рдп dev рдЖрдгрд┐ prod рдЪреЗ рдЖрд░рд╛рдЦрдбреЗ рджреЛрди рдкреНрд░рддреА рдЕрд╕рддрд╛рдд рдЬреНрдпрд╛ рд╣рд│реВрд╣рд│реВ рд╡реЗрдЧрд│реНрдпрд╛ рд╣реЛрддрд╛рдд. Loops
рд╢рд┐рд╡рд╛рдп рд╡реАрд╕ lockers рдореНрд╣рдгрдЬреЗ copy рдХреЗрд▓реЗрд▓реЗ рд╡реАрд╕ blocks. рдЖрдгрд┐ count рд╡ for_each рдпрд╛рдВрддреАрд▓ рдирд┐рд╡рдб рдореНрд╣рдгрдЬреЗ
"рдордзрд▓реА item рдХрд╛рдврдгреЗ" рд╣рд╛ рдЫреЛрдЯрд╛ рдмрджрд▓ рдард░рдгреЗ рдЖрдгрд┐ рдЪреБрдХреАрдЪреНрдпрд╛ objects рдЪреА рд▓реЗрдмрд▓реЗ рдмрджрд▓рдгрд╛рд░реА, рддреНрдпрд╛рдВрдирд╛ рдкреБрдиреНрд╣рд╛ рдмрд╛рдВрдзрдгрд╛рд░реА
рдХрд┐рдВрд╡рд╛ destroy рдХрд░рдгрд╛рд░реА рдЙрд▓рдерд╛рдкрд╛рд▓рде рдард░рдгреЗ рдпрд╛рдВрддреАрд▓ рдлрд░рдХ. Data рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЦрд▒реНрдпрд╛ objects рд╕рд╛рдареА
тАФ disks, users, DNS records тАФ рддреА рдЙрд▓рдерд╛рдкрд╛рд▓рде рдореНрд╣рдгрдЬреЗ outage.
ЁЯФз рдХрд╕реЗ (рдпрд╛ repo рдордзреНрдпреЗ)
iac/engine.py рдордзреАрд▓ expand() рдЖрдзреА variable рд╡рд╛рдЪрддреЗ (default рдЖрдгрд┐
рдРрдЪреНрдЫрд┐рдХ validation рд╕рд╣), рдордЧ locals, рдордЧ рдкреНрд░рддреНрдпреЗрдХ resource. count рдЕрд╕рд▓реЗрд▓реНрдпрд╛ block рдЪреЗ
instances [0], [1], тАж рд╣реЛрддрд╛рдд; for_each рд╕рд╣ instances ["key"]. resolve()
${var.x}, ${local.x}, ${count.index}, ${each.key}, ${var.labels[count.index]},
${length(var.labels)} рдЖрдгрд┐ references рднрд░рддреЗ. Outputs apply рдирдВрддрд░ resolve рд╣реЛрддрд╛рдд;
show_outputs() рддреЗ terraform output рд╕рд╛рд░рдЦреЗ print рдХрд░рддреЗ.
ЁЯзк рдХрд░реВрди рдкрд╛рд╣рд╛
python3 iac/demo.py variables
python3 - <<'EOF'
import sys; sys.path.insert(0, "iac")
from cloud import FakeCloud
from engine import plan, apply, ConfigError
def cfg(kind):
lock = ({"count": "${length(var.labels)}", "label": "${var.labels[count.index]}"} if kind == "count"
else {"for_each": "${var.labels}", "label": "${each.key}"})
return {"variable": {"labels": {"default": ["a", "b", "c", "d"]},
"seats": {"default": 30, "validation": (lambda v: 10 <= v <= 60, "seats must be between 10 and 60")}},
"resource": {"school_room": {"lab": {"name": "lab", "seats": "${var.seats}"}},
"school_locker": {"row": dict(lock, room_id="${school_room.lab.id}")}}}
for kind in ("count", "for_each"):
cloud = FakeCloud(); st, _, _ = apply(plan(cfg(kind), None, cloud), cloud)
p = plan(cfg(kind), st, cloud, vars={"labels": ["b", "c", "d"]})
print(f"{kind:<8} remove 'a' тЖТ {p.summary()}")
for line in p.lines(): print(" ", line.strip())
try: plan(cfg("count"), None, FakeCloud(), vars={"seats": 500})
except ConfigError as e: print("seats=500 тЖТ", e)
EOF
тЬЕ рддрдкрд╛рд╕рд╛ тАФ рддреБрдореНрд╣рд╛рд▓рд╛ рдХрд╛рдп рджрд┐рд╕рд╛рдпрд▓рд╛ рд╣рд╡реЗ
variables рдЕрд╕реЗ print рдХрд░рддреЗ:
output lab_name = "dev-school-lab"
output locker_ids = ["locker-01", "locker-02", "locker-03"]
тФАтФА -var env=prod -var seats=40 тЖТ ~ school_room.lab name: 'dev-school-lab' тЖТ 'prod-school-lab' ┬╖ seats: 30 тЖТ 40
тФАтФА count: remove 'b' from [a, b, c] тЖТ ~ school_locker.row[1] label: 'b' тЖТ 'c' ┬╖ - school_locker.row[2] (destroy) тЖТ Plan: 0 to add, 1 to change, 1 to destroy.
тФАтФА for_each: remove 'b' тЖТ - school_locker.row["b"] (destroy) тЖТ Plan: 0 to add, 0 to change, 1 to destroy.
рддреБрдордЪрд╛ snippet рдЕрд╕реЗ print рдХрд░рддреЛ:
count remove 'a' тЖТ Plan: 0 to add, 3 to change, 1 to destroy.
~ school_locker.row[0] label: 'a' тЖТ 'b'
~ school_locker.row[1] label: 'b' тЖТ 'c'
~ school_locker.row[2] label: 'c' тЖТ 'd'
- school_locker.row[3] (destroy)
for_each remove 'a' тЖТ Plan: 0 to add, 0 to change, 1 to destroy.
- school_locker.row["a"] (destroy)
seats=500 тЖТ Invalid value for variable "seats": seats must be between 10 and 60
ЁЯПБ рддреБрдореНрд╣реА рдЖрддреНрддрд╛рдЪ рдХрд╛рдп рд╕рд┐рджреНрдз рдХреЗрд▓реЗ
count рд╕рд╣ рдЪрд╛рд░ lockers рдкреИрдХреА рдкрд╣рд┐рд▓рд╛ рдХрд╛рдврд▓реНрдпрд╛рд╡рд░ рдЪрд╛рд░рд╣реА addresses рдирд╛ рдзрдХреНрдХрд╛ рд▓рд╛рдЧрд▓рд╛: рддреАрди
рд▓реЗрдмрд▓-рдмрджрд▓ рдЖрдгрд┐ рдПрдХ destroy тАФ рдЖрдгрд┐ destroy рдЭрд╛рд▓реЗрд▓рд╛ object рдЦрд░рд╛ locker d рд╣реЛрддрд╛, a рдирд╛рд╣реА.
for_each рд╕рд╣ рддреЛрдЪ рдмрджрд▓ рдореНрд╣рдгрдЬреЗ рдиреЗрдордХрд╛ row["a"] рдЪрд╛ рдПрдХ destroy. рдпрд╛ campus рдордзреНрдпреЗ рд▓реЗрдмрд▓ рдмрджрд▓рдгреЗ
рдЬрд╛рдЧреАрдЪ update рдЖрд╣реЗ; рд╕рд░рдХрд▓реЗрд▓реНрдпрд╛ field рдордзреНрдпреЗ ForceNew argument рдЕрд╕рддрд╛ рддрд░ рдкреНрд░рддреНрдпреЗрдХ рд▓реЗрдмрд▓-рдмрджрд▓
replace рдард░рд▓рд╛ рдЕрд╕рддрд╛. Validation рдиреЗ рдХреЛрдгрддрд╛рд╣реА plan рдмрдирдгреНрдпрд╛рдЖрдзреАрдЪ seats = 500 рдерд╛рдВрдмрд╡рд▓реЗ.
тЪая╕П рдиреЗрд╣рдореАрдЪреНрдпрд╛ рдЪреБрдХрд╛
- рд╕реНрд╡рддрдГрдЪреА рдУрд│рдЦ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ рдЧреЛрд╖реНрдЯреАрдВрдЪреНрдпрд╛ list рд╡рд░
count(users, buckets, disks) тАФfor_eachрд╡рд╛рдкрд░рд╛ - рд╕рд╛рдзреНрдпрд╛ list рд╡рд░
for_eachтАФ Terraform рд▓рд╛ map рдХрд┐рдВрд╡рд╛ set рд╣рд╡рд╛:toset(var.labels) - рдлрдХреНрдд apply рдирдВрддрд░ рдХрд│рдгрд╛рд▒реНрдпрд╛ рдореВрд▓реНрдпрд╛рд╡рд░реВрди (рдПрдХ id) рдмрдирд╡рд▓реЗрд▓реЗ
countрдХрд┐рдВрд╡рд╛for_eachтАФ Terraform рдирдХрд╛рд░ рджреЗрддреЗ: plan рдЪреНрдпрд╛ рд╡реЗрд│реАрдЪ keys рдорд╛рд╣реАрдд рдЕрд╕рд╛рдпрд▓рд╛ рд╣рд╡реНрдпрд╛рдд - рд╕рдЧрд│реНрдпрд╛рд╕рд╛рдареА рдПрдХрдЪ рдкреНрд░рдЪрдВрдб variable object, types рдХрд┐рдВрд╡рд╛ validation рд╢рд┐рд╡рд╛рдп
- git рдордзреНрдпреЗ variable defaults рдореНрд╣рдгреВрди secrets тАФ рддреЗ secret store рдордзреВрди рджреНрдпрд╛ (рдЖрдгрд┐ рдзрдбрд╛ 05 рдкрд╛рд╣рд╛)
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд
рдЦрд▒реНрдпрд╛ account рд╡рд░ тАФ validation рдЕрд╕рд▓реЗрд▓реЗ typed variables, рдПрдХ .tfvars file, рдЖрдгрд┐ map рд╡рд░
for_each:
variable "env" {
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.env)
error_message = "env must be dev, staging or prod."
}
}
variable "lockers" {
type = map(object({ size = string }))
default = { a = { size = "m" }, b = { size = "s" } }
}
locals {
prefix = "${var.env}-school"
}
resource "aws_s3_bucket" "locker" {
for_each = var.lockers
bucket = "${local.prefix}-locker-${each.key}"
tags = { size = each.value.size }
}
output "locker_buckets" {
value = { for k, b in aws_s3_bucket.locker : k => b.arn }
}
terraform plan -var-file=prod.tfvars # prod.tfvars: env = "prod"
TF_VAR_env=dev terraform plan # the same through an environment variable
terraform output -json # outputs for scripts
terraform console # try expressions: > length(var.lockers)
рдЖрдзреАрдЪ count рд╡рд╛рдкрд░рд▓реЗ рдЖрд╣реЗ рдЖрдгрд┐ рдмрджрд▓рд╛рдпрдЪреЗ рдЖрд╣реЗ? moved block (рдзрдбрд╛ 09)
aws_s3_bucket.locker[0] рдЪреЗ aws_s3_bucket.locker["a"] рдЕрд╕реЗ рдирд╛рд╡ рдкреБрдиреНрд╣рд╛ рди рдмрд╛рдВрдзрддрд╛ рдмрджрд▓рддреЛ.
ЁЯПн рдкреНрд░рддреНрдпрдХреНрд╖ рд╡рд╛рдкрд░рд╛рдд рд╣реЗ рдХрд╛ рдорд╣рддреНрддреНрд╡рд╛рдЪреЗ: data рдХрд┐рдВрд╡рд╛ рдУрд│рдЦ рдЕрд╕рд▓реЗрд▓реНрдпрд╛ resources рд╡рд░реАрд▓
count =рд╕рд╛рдареА рддреБрдордЪрд╛ code grep рдХрд░рд╛. рдкреНрд░рддреНрдпреЗрдХ рдЬрдг рднрд╡рд┐рд╖реНрдпрд╛рддреАрд▓ "рдордзрд▓реА item рдХрд╛рдврд╛" incident рдЖрд╣реЗ; list рдЕрдЬреВрди рд▓рд╣рд╛рди рдЕрд╕рддрд╛рдирд╛рдЪ рддреНрдпрд╛рдВрдирд╛movedblocks рд╕рд╣for_eachрд╡рд░ рд╣рд▓рд╡рд╛.
тПня╕П рдкреБрдвреЗ
рдХрдВрддреНрд░рд╛рдЯрджрд╛рд░ рдЧреБрдкрдЪреВрдк рдПрдХ рд╡рд╣реА рдареЗрд╡рдд рдЖрд▓рд╛ рдЖрд╣реЗ: рдХреЛрдгрддрд╛ address рдХреЛрдгрддрд╛ рдЦрд░рд╛ object рдЖрд╣реЗ. рдкреБрдвреЗ: state тАФ рддреА рдХрд╛рдп рдЖрд╣реЗ, рдЖрдгрд┐ рддрд┐рдЪреЗ рд╕рдВрд░рдХреНрд╖рдг рдХрд╛ рдХрд░рд╛рдпрд▓рд╛рдЪ рд╣рд╡реЗ.
git checkout lesson-05-state