Terraform Associate: encoding functions jsonencode and collection helpers
Encode documents. Merge tags. Name keys. Know which function you called.
<!-- hal:authoritative:yaml -->
Encode when the API wants a document. Merge when tags layer. Name keys when for_each needs a map. Know which function you called.
§I. Frame: Associate objectives for tonight
09-29 covered lifecycle. 09-26 covered type constraints. 09-23 covered data sources. 09-20 covered dynamic blocks. 09-17 covered ephemeral. 09-14 covered import and moved. 09-08 covered check blocks. Leave all of that.
Tonight is encoding and collection helpers: jsonencode, jsondecode, templatefile, keys, values, merge, tomap. Ops declares a CloudWatch log group and a metric filter. Dev draws the Encode Shelf. The drills below use terraform console so every expression is real and credential-free.
§II. Encoding shelf: three jobs
| Function | Job | Exam trap |
|---|---|---|
jsonencode(value) | HCL value → JSON string | Does not produce CloudWatch filter grammar; produces JSON |
jsondecode(string) | JSON string → HCL value | Inverse of encode; Lab 20 spine |
templatefile(path, vars) | File + map → string | Inside modules use path.module; root-relative paths break |
Named technique: Encode Shelf. Before you encode, name the consumer (JSON document, on-disk template, or domain grammar). Pick the matching function. Do not jsonencode a CloudWatch filter pattern and expect Logs to accept it.
§III. jsonencode: types survive
Open an empty directory, terraform console, and evaluate:
jsonencode({ retention = 30, never_expire = false, tags = ["app", "prod"] })
Terraform 1.14.3 prints a compact JSON string. Numbers stay numbers. false stays boolean. Lists stay arrays. That matters for IAM policies and for any attribute that parses JSON strictly.
Fact one. jsonencode("hello") yields "\"hello\"" (a JSON string, quotes included). String-in, string-out is still encoding.
Fact two. jsondecode(jsonencode(local.obj)) round-trips objects for exam purposes. Real configs usually encode once toward AWS and decode once from file() or a secret.
Fact three. Brikman 3e p.338 shows jsondecode on a secret string. Encoding is the write-side twin for documents you author in HCL.
§IV. Collection helpers: keys, values, merge, tomap
locals {
base_tags = { team = "platform", env = "prod" }
extra = { service = "api", env = "staging" } # env collision
merged = merge(local.base_tags, local.extra)
}
merge overlays maps left to right. Later keys win. Here env becomes "staging". Lab 27 drills this for tag layers.
keys({ a = 1, b = 2 }) # ["a", "b"] (lexicographic in current Terraform)
values({ a = 1, b = 2 }) # [1, 2] (same key order as keys)
tomap({ a = "1", b = "2" }) # forces map type from an object constructor when needed
Rule one. keys / values order matches across a single map. Do not zip keys(A) with values(B).
Rule two. merge does not deep-merge nested objects. Nested maps replace as a whole.
Rule three. tomap is for type constraints and ambiguous constructors. Prefer an explicit map(...) type on variables when you control the interface (pair with 09-26 type constraints without redoing that lesson).
§V. CloudWatch surface on the exam
Ops sets retention_in_days and a metric filter pattern. Exam stems that touch tonight:
- Which function turns a map into an IAM policy JSON string? →
jsonencode. - **Which function renders
user-data.shwith${app}?** →templatefile. - **A module calls
templatefile("user-data.sh", ...)and breaks when called from another root.** → use"${path.module}/user-data.sh"(Brikman pp.190-192). - Layer global tags under service tags with service winning on collision. →
merge(global, service). - **Build
for_eachkeys from a map of log groups.** →keys(var.groups)or iterate the map directly withfor_each = var.groups.
Trap: choosing jsonencode for a CloudWatch filter pattern that must be { $.level = "ERROR" }. That pattern is domain grammar. Dev Encode Shelf rule 3.
§VI. Five drills (answers below)
D1. jsonencode({ a = 1, b = true }) contains the substring true without quotes. True or false?
D2. merge({a=1},{a=2}).a evaluates to what?
D3. Inside a child module, the durable path form for a template next to main.tf is what expression?
D4. Name the inverse of jsonencode used in Lab 20 when reading a JSON file.
D5. Why is jsonencode({ level = "ERROR" }) the wrong producer for aws_cloudwatch_log_metric_filter.pattern?
Answers
D1. True. JSON boolean, not the string "true".
D2. 2. Later map wins.
D3. templatefile("${path.module}/…", { … }).
D4. jsondecode (often composed with file).
D5. Filter patterns use CloudWatch grammar, not a JSON object document. Encode Shelf: domain grammar consumer.
§VII. Close
Encoding functions move values across the HCL/JSON and HCL/file boundaries. Collection helpers shape maps for tags and for_each. Ops owns retention and the Rust census. Dev owns when to encode versus when to keep domain grammar as a string. Maghrib owns the quiz.
Related
- Tome: Brikman 3e pp.190-192 (
templatefile) — grounded-in; p.338 (jsondecode) — referenced - Bootcamp: Lab 20 · Lab 27 — grounded-in
- Prior Cert: lifecycle 09-29 · type constraints 09-26 · dynamic/for_each 09-20