Terraform for Expression Tester

Try a for expression against real data. The failure worth finding here is the one that only appears when two elements produce the same key, which small test data never does.

Paste below, or drop a file anywhere on this panel

Or drop a file anywhere on this panel. Nothing is uploaded: the analysis runs in this tab.

The answer appears here

Paste on the left and press Test. Nothing leaves this tab.

Examples

Real input you can load into the tool above. Each one shows a different thing going wrong, because that is what the tool is for.

A key collision

Two users share a first name, and the expression only fails once the data contains one

users = ["alice.smith", "bob.jones", "alice.chen"]

{ for u in users : split(".", u)[0] => u }

The same expression with grouping

Collisions become lists, which changes the value type for whatever consumes it

users = ["alice.smith", "bob.jones", "alice.chen"]

{ for u in users : split(".", u)[0] => u... }

A map result

Every key here becomes part of a resource address if this feeds a for_each

items = ["a", "b"]

{ for i in items : i => upper(i) }

Common mistakes

These are the ones that fail silently. The config is accepted, nothing raises an error, and the consequence arrives later.

  1. Keying a for_each map by something not guaranteed unique

    Two elements produce the same key and Terraform stops, or with the grouping symbol they silently merge. Either way it appears the first time the data collides, long after the code was written.

    Instead:Key by something unique by construction, such as an id, or add ... and handle the list.

  2. Using a list index as a for_each key

    The key is part of the resource address, so a change in list order renames every resource after the change, which Terraform reads as destroy and create.

    Instead:Key by a stable attribute of the element itself.

  3. Expecting a for expression over a map to keep insertion order

    Map iteration is by sorted key, so a list built from a map comes out sorted rather than in the order you wrote it.

    Instead:Build the list directly if the order is meaningful.

A duplicate key is not a crash, unless it is

It depends on which form you wrote, and the two forms look almost identical.

Square brackets give a list, braces with => give a map

[for x in c : e] produces a list or set. { for k, v in c : k => v } produces a map. Only the map form can key a for_each meaningfully, and only the map form can produce a key collision.

A collision in a map form is an error you might never see

Terraform reports that two elements produced the same key, and stops. That is the good outcome, and it only happens when the data actually collides, so an expression that works for months breaks when somebody adds a user whose first name matches another's.

The ... grouping symbol turns collisions into lists

{ for u in users : dept(u) => u... } collects everything sharing a key into a list rather than erroring. That changes the value type, so anything consuming it has to handle a list, which is the trade being made rather than a free fix.

These keys become resource addresses

If the result feeds a for_each, every key appears in state as resource["key"], in every plan line, and in every state command anyone types. A key derived from an index or from anything that changes destroys and recreates the resource when it changes.

An if filter goes at the end, after the value

[for x in c : x if cond]. It is easy to read as filtering the input; it filters the output, which matters when the value expression itself would fail on the elements being filtered out, because it still runs on them first.

Iterating a map gives sorted keys

Which means the output order of a list built from a map is sorted by key rather than by the order the map was written. For a list this is visible; for a map result it does not matter.

More terraform tools

Terraform Validator & Formatter Does it parse, and is it formatted? Terraform dynamic Block Generator It is ingress.value, not each.value Terraform check and precondition Generator A failed check does not stop the apply Terraform variables.tf Generator From the var. references you already wrote Terraform Unused Declaration Linter Dead variables, locals and data sources Terraform Atlantis YAML Generator when_modified misses your modules Terraform Function Tester and HCL Console terraform console, in a tab Terraform Interpolation Tester null becomes an empty string Terraform templatefile Tester Render it before you apply it Terraform Resource Count Calculator How many objects, not how many blocks Terraform count to for_each Converter With the moved blocks it needs Terraform moved Block Generator Rename without destroying Terraform state mv Command Builder No plan, no undo Terraform target Flag Builder And why to stop using it Terraform outputs.tf Generator Outputs are not sensitive by default Terraform Remote State Data Source Generator It grants read of the whole state Terraform Provider Block Generator docker is not hashicorp/docker Terraform .gitignore Generator and Checker .terraform* eats the lock file Terraform to OpenTofu Migration Checker The state is the part to watch Terraform tfvars to JSON Converter The JSON file wins Terraform Version Constraint Parser What ~> actually allows Terraform Resource Address Parser Quote it before the shell eats it Terraform cidrsubnet Calculator newbits is added, not the target Terraform Import Block Generator The id is not the name Terraform Lock File Viewer Why CI fails and your laptop does not Terraform Variables Checker A missing value hangs CI Terraform tfvars to Env Vars Converter Lists and maps have to be JSON Terraform Deprecated Syntax Checker What a current Terraform rejects Terraform State File Viewer Read it without uploading it Terraform Plan JSON Viewer It is not redacted Terraform HCL to JSON Converter And what the conversion loses Terraform Module Source Validator version only works for the registry Terraform Backend Config Decoder Is state actually locked? Terraform Naming Convention Validator A hyphen makes a resource unreferenceable

Elsewhere on the site