Skip to content
Français
Français
medium

61 free Terraform Associate practice labs

2 min read

61 hands-on labs to practise for the HashiCorp Certified: Terraform Associate (003/004), roughly 41 hours of work, each one graded on the state of your machine rather than on the commands you typed. They run on your own hardware with the `dsoxlab` command line, and cost nothing.

A question-based exam, not a hands-on one: it covers vocabulary, the resource lifecycle, state and modules. The labs are there to make sense of what the questions describe.

  • 61labs
  • 41hours of practice

Levels 15 beginner 42 intermediate 4 advanced

Official exam objectives published by HashiCorp

These are not exam questions. They are hands-on exercises on the skills the exam measures, graded on the state of your machine.

Terraform, Associate and Professional

61 labs every lab replayed and scored

Add this catalogue dsoxlab catalog add https://github.com/stephrobert/terraform-dsoxlab-training

Discover Terraform

  • Prove that Terraform has a memory

    A beginner believes Terraform re-reads the `.tf` files and queries the infrastructure on every command, the state being an accessory cache. Take that idea apart on a purely local configuration: prove a second apply changes nothing, that drift caused outside Terraform is detected without losing the identity of the untouched resources, that data merely read is not a managed resource, and that an object nobody declared stays invisible.

    dsoxlab start getting-started-terraform-overview

    30m beginner shell Companion guide (French)

  • Prove idempotence where the imperative script diverges

    A provisioning script is provided, and it diverges as soon as you replay it: a new identifier every time, a report that piles up instead of being replaced. Reach the same result in Terraform, then establish by the plan that a second pass is a non-event, and repair an external drift without touching your code.

    dsoxlab start getting-started-declarative-vs-imperative

    30m beginner shell Companion guide (French)

  • Prove Terraform / OpenTofu compatibility, and where it stops

    "The two tools are compatible" is repeated everywhere and checked by nobody. Make a configuration portable by declaring the source and version constraint of every provider, then establish by the structured state that both binaries read the same state, resolve the same versions on two different registries, and that the lock file is exactly where portability stops.

    dsoxlab start getting-started-terraform-vs-opentofu

    30m beginner shell Companion guide (French)

  • Pin the CLI version and lock the providers

    Installing the binary proves almost nothing. Constrain the CLI version from the configuration itself, pin the three providers, make an impossible constraint refuse to run, and prove the lock file rules: wipe `.terraform/`, re-init, and the resolved versions must not move.

    dsoxlab start getting-started-install-terraform

    30m beginner shell Companion guide (French)

  • Make fmt, validate and the outputs agree

    Three faults are planted: a file outside canonical format, an orphan reference that fails `validate`, and missing outputs. Fix all three without changing what the configuration produces, then prove an expression recomputes outside the state.

    dsoxlab start getting-started-cli-terraform

    30m beginner shell Companion guide (French)

  • Read a plan before applying it

    An already applied configuration must change. Before touching anything, say which of its resources will be updated in place and which will be destroyed then recreated. The human-facing plan announces both in the same block of text: the only reliable reading is the actions field of the plan converted to JSON. Then apply exactly the plan you read, from a saved plan file.

    dsoxlab start getting-started-terraform-workflow

    30m beginner shell Companion guide (French)

  • What Terraform manages, and what it merely reads

    One word separates the two kinds of block: `resource` creates and manages, `data` reads. Prove it in the `mode` field of state, then observe the two consequences: a `destroy` does not touch what it did not create, and a data source is re-read on every plan.

    dsoxlab start getting-started-providers-resources-data-sources

    30m beginner shell Companion guide (French)

  • Split a monolith without moving the plan

    File names have no functional effect: Terraform reads every `.tf` as one document. Split a monolith into six thematic files and prove, by comparing two JSON plans, that nothing changed. Then observe which value source wins.

    dsoxlab start getting-started-terraform-project-structure

    30m beginner shell Companion guide (French)

First infrastructures

  • First infrastructure: the full cycle, proven

    Run the Terraform cycle on a REAL resource rather than reciting it, and learn on the way that `~> 0.8` does not forbid `0.9.x`: the pessimistic operator increments the rightmost component, and the 0.9 branch rewrote the resource schema.

    dsoxlab start first-infra-first-infrastructure

    30m beginner shell Companion guide (French)

  • Variables, locals and the real precedence order

    Four rungs, checked in order: `default`, `TF_VAR_`, `terraform.tfvars`, `*.auto.tfvars`, `-var`. The decisive one is the second: a values file BEATS the environment variable, the rank almost everyone places too high.

    dsoxlab start first-infra-variables-outputs

    30m beginner shell Companion guide (French)

  • The dependency Terraform cannot guess

    Terraform builds its graph from the REFERENCES it finds in expressions. Nine times out of ten a hand-written `depends_on` signals a missing reference. This lab is about the tenth: a block that consumes a variable and references nothing, where Terraform is free to act in the wrong order.

    dsoxlab start first-infra-virtual-network

    30m beginner shell Companion guide (French)

  • Update in place or replacement: read it in the plan

    On a virtual machine, some attributes change in place and others destroy then recreate the machine. The plan says which, before the apply, provided you know where to look. Then verify the plan told the truth, by applying it.

    dsoxlab start first-infra-vm-libvirt

    30m beginner shell Companion guide (French)

  • Produce an Ansible inventory from the state

    Terraform knows what it created; Ansible must know it too. Build the bridge, and make it RELIABLE rather than merely functional: a typed variable, addresses computed by an HCL function, a serialised file, and an inventory that disappears with the fleet it describes.

    dsoxlab start first-infra-ansible

    30m beginner shell Companion guide (French)

  • Resume after a failed apply, without redoing the work

    An apply that fails halfway leaves a PARTIAL state. It is not an accident to wipe: it is the starting point of the repair. Fix the cause in the configuration, and prove the already created resources kept their identifiers.

    dsoxlab start first-infra-debug-apply

    30m beginner shell Companion guide (French)

  • Destroying cleanly, and the four things it covers

    Destroying is not one operation. Observe four that do not resemble each other: the global destroy a guard can REFUSE, the targeted destroy, the removal of a resource FROM THE CODE, and the full destroy that empties the state without deleting its file.

    dsoxlab start first-infra-clean-destroy

    30m beginner shell Companion guide (French)

Writing Terraform code

  • Explicit source and provider alias

    Declare providers correctly: an explicit source address that resolves to registry.terraform.io/hashicorp/random, a second configuration of the same provider distinguished by an alias, and a resource wired to it with provider =. Proven by version -json and the plan JSON provider_config. Associate 5b.

    dsoxlab start write-code-providers

    35m intermediate shell Companion guide (French)

  • Read a resource's lifecycle in the plan

    Build four resources ordered only by references, then prove in the plan JSON the four lifecycle operations: update in place, replace, create before destroy, and destroy. Pro objective 1c.

    dsoxlab start write-code-declare-resources

    45m intermediate shell Companion guide (French)

  • Variables typing, validation and precedence

    Type variables correctly and master their traps: a map of objects with optional() defaults, a validation rule that rejects before any provider, an explicit null that falls back to the default with nullable=false, the real source precedence (auto.tfvars over TF_VAR_, -var above all), and sensitive as a display mask, not a state protection. Associate 2e plus 2f.

    dsoxlab start write-code-variables

    45m intermediate shell Companion guide (French)

  • The secret the output does not hide

    Master the real output block: a type constraint (object), sensitivity propagation (an output referencing a secret must be sensitive, and sensitive is only a display mask, cleartext stays in state, -json and -raw), nonsensitive() to expose a hash deliberately, and a precondition that fails the plan. Associate 2e plus 2f.

    dsoxlab start write-code-outputs

    45m intermediate shell Companion guide (French)

  • The local that is not resolved at plan time

    Centralize expressions in locals blocks: normalize with HCL functions, keep a ternary typed, generate a list with a for expression, derive from a resource attribute (known after apply), and inherit a variable's sensitivity. Pro objective 2c.

    dsoxlab start write-code-locals

    45m intermediate shell Companion guide (French)

  • When exactly does Terraform read a data source?

    Build three data sources whose read timing is deliberate: one read during the plan, one deferred to the apply because the resource it depends on is changing, and one carrying an explicit depends_on that does NOT defer it. Prove each case in the plan JSON. Pro objective 2b.

    dsoxlab start write-code-data-sources

    45m intermediate shell Companion guide (French)

  • What expressions really compute

    Write correct Terraform expressions: reference a managed resource with no prefix, know that == does not convert types (so 3 is not "3") while arithmetic does, respect operator precedence, and use null to omit an argument rather than an empty string. Associate 2e.

    dsoxlab start write-code-expressions

    30m intermediate shell Companion guide (French)

  • Compose values with HCL functions

    Deduplicate a collection for for_each, handle out-of-range element(), lookup() fallback, ceil() rounding, template escaping and a provider-defined function. Pro objective 2c.

    dsoxlab start write-code-functions

    45m intermediate shell Companion guide (French)

  • The configuration that refuses absurd values

    Compute values with conditional expressions, then place each guard at the right level: validation, precondition, postcondition and check block. Pro objective 2a.

    dsoxlab start write-code-conditionals

    45m intermediate shell Companion guide (French)

  • Custom conditions: precondition, postcondition and check blocks

    Validate a configuration with the language features designed for it. Pro objective 2a.

    dsoxlab start write-code-validation-check-preconditions

    30m intermediate shell Companion guide (French)

  • count indexes by position, and the position lies

    Choose count or for_each per resource and prove it: index a set by name with for_each, migrate a count resource to for_each with no destroy using moved blocks, keep count for interchangeable copies, and expose a conditional resource with one(). Associate objective 4b plus the Professional moved migration.

    dsoxlab start write-code-count

    45m intermediate shell Companion guide (French)

  • Add an instance without destroying the others

    Migrate count to for_each with moved blocks on an already applied configuration, then add a service proving zero destruction. Pro objective 2d.

    dsoxlab start write-code-for-each

    45m intermediate shell Companion guide (French)

  • Transform a catalog with for expressions

    Derive four collections from a single map of servers with for expressions: a filtered tuple, an object grouped by role with the ellipsis, a flattened two-level cross, and a filtered for_each. Avoid the splat-on-a-map trap. Pro objective 2c.

    dsoxlab start write-code-for-loops

    45m intermediate shell Companion guide (French)

  • Generate blocks, and know when not to

    Generate cloudinit parts with a dynamic block filtered in for_each and named from the key, keep the fixed part literal, and hit the wall: a lifecycle meta-argument block cannot be generated by dynamic. Pro objective 2d.

    dsoxlab start write-code-dynamic-blocks

    45m intermediate shell Companion guide (French)

  • depends_on belongs only where a reference cannot go

    Remove a redundant depends_on that a reference already covers, replace a hardcoded path with a reference, keep the one depends_on nothing else can express, and prove the graph in the plan JSON. Pro objective 2d.

    dsoxlab start write-code-depends-on

    45m intermediate shell Companion guide (French)

  • The lifecycle block decides the order, not you

    Set create_before_destroy and watch its propagation go down to dependencies, hit the documented limit of prevent_destroy, scope ignore_changes to a single attribute, trigger a replacement from a bare value through terraform_data, and reject an invalid input at plan time. Pro objective 2d.

    dsoxlab start write-code-lifecycle

    45m intermediate shell Companion guide (French)

  • The typo that breaks nothing

    Two tfvars traps: an undeclared variable in a .tfvars is only a warning, so the real variable silently stays at its default (fix the typo), and terraform.tfvars.json outranks terraform.tfvars (a distinct precedence level). Associate 3c.

    dsoxlab start write-code-tfvars-files

    25m intermediate shell Companion guide (French)

  • Version constraints and the lock file

    Write correct version constraints: a literal required_version (the terraform block accepts no variable), an exact provider pin that resolves to that version, and the pessimistic ~> that allows 3.x but not 4.0. Proven by version -json and the h1 hashes of the lock file. Associate 3a.

    dsoxlab start write-code-version-constraints

    30m intermediate shell Companion guide (French)

  • The config that works but no CI accepts

    Take a working but non-conforming config and make it CI-ready: pass terraform fmt, fix the orphan reference that breaks validate, rename resources to snake_case, type and describe variables and outputs, mark the token sensitive, type the number output (1.15), and write a .gitignore that excludes state but keeps the lock file. Associate 2a plus 2e.

    dsoxlab start write-code-style-guide

    40m intermediate shell Companion guide (French)

  • When sensitivity breaks for_each

    A side effect of sensitive rarely taught: a sensitive value cannot be a for_each key (Invalid for_each argument), so you must iterate over a non-sensitive set. The contaminated resource attribute is flagged in sensitive_values, and a hash is exposed with nonsensitive(). Professional 2f.

    dsoxlab start write-code-sensitive-data-sensitive-values

    30m intermediate shell Companion guide (French)

  • The value that never touches the state

    Declare an ephemeral value that is generated during the run but never written to the state or plan: the ephemeral block (random 3.7+), the contrast with a persisted random_password, exposing it safely at the root with ephemeralasnull(), and the two errors it triggers when leaked. Professional 2f.

    dsoxlab start write-code-sensitive-data-ephemeral-values

    35m intermediate shell Companion guide (French)

  • Write-only arguments: a secret that never lands in the state

    Send a secret to the provider without ever persisting it in the Terraform state, using write-only arguments against a local AWS emulator.

    dsoxlab start write-code-sensitive-data-write-only-arguments

    30m intermediate shell Companion guide (French)

Terraform state

  • Take over a secret already in service, without regenerating it

    A plain `apply` would draw a new password. Attach the existing one through an `import` block, then observe that the secret sits in clear text in the state while being marked `sensitive_attributes`: masking is a display, not encryption.

    dsoxlab start state-understand-state

    30m intermediate shell Companion guide (French)

  • Migrate the state without breaking its lineage

    A backend block accepts no named value. Move from an implicit local backend to a parameterised one through partial configuration and `-backend-config`, and prove the state migrated rather than being recreated: same `lineage`, both resources still managed.

    dsoxlab start state-backends

    30m intermediate shell Companion guide (French)

  • State locking, what it really blocks

    Measure the exact scope of the state lock on a local backend, and state what enables it on S3.

    dsoxlab start state-state-locking

    30m intermediate shell Companion guide (French)

  • terraform state list, the address is the identity

    Find the address of an instance when all you know is its real identifier, and count managed resources without being caught by a module.

    dsoxlab start state-terraform-state-list

    30m intermediate shell Companion guide (French)

  • terraform state show, and what the summary hides

    Extraire du state une valeur sensible et des attributs nuls, que la sortie humaine de state show caviarde ou omet.

    dsoxlab start state-terraform-state-show

    30m intermediate shell Companion guide (French)

  • terraform state mv and the moved block, refactor without destroying

    Reattach three existing objects to their new addresses, imperatively then declaratively, and prove nothing was recreated.

    dsoxlab start state-terraform-state-mv

    30m intermediate shell Companion guide (French)

  • terraform state rm and the removed block, stop managing without destroying

    Take two resources out of the state without deleting the files, and learn why a removed block destroys unless you write destroy = false.

    dsoxlab start state-terraform-state-rm

    30m intermediate shell Companion guide (French)

  • Restore an amputated state: pick the right backup, prove nothing was recreated

    A state rm went wrong and the backup was overwritten. Three candidate backups, one restorable, compared by lineage and serial, then pushed without touching the real files.

    dsoxlab start state-backup-restore-state

    40m intermediate shell Companion guide (French)

  • Diagnose a drift and adopt an orphan resource, without losing its value

    Prove a drift with a saved refresh-only plan, reconcile it, then adopt a pre-existing token with an import block, without Terraform regenerating it.

    dsoxlab start state-diagnose-state

    40m intermediate shell Companion guide (French)

Terraform modules

  • A reusable module configures no provider of its own

    Empty a child module of its provider configuration, have it declare the aliased configuration it expects, pass that configuration from the root, and instantiate the module three times with a single for_each.

    dsoxlab start modules-create-modules

    45m intermediate shell Companion guide (French)

  • Refactor a monolith into the standard module structure

    Split a single .tf file into the official layout, add a nested module called by relative path, a standalone example, and prove that override.tf is the one file name with a functional effect.

    dsoxlab start modules-module-structure

    45m intermediate shell Companion guide (French)

  • A module interface is a contract, not just types

    Make an object attribute optional, refuse an explicit null, reject an out-of-range value, gate an output behind a precondition, and re-export a secret without breaking the plan.

    dsoxlab start modules-module-variables-outputs

    45m intermediate shell Companion guide (French)

  • A local module is read in place, not installed

    Wire two root projects onto one shared local module, and prove from modules.json which folder each call really reads: relative path versus absolute path.

    dsoxlab start modules-module-local

    45m intermediate shell Companion guide (French)

  • A registry module is downloaded, versioned, and never locked

    Pin a registry module, resolve a flexible constraint, and prove from modules.json and the plan JSON which version got installed: the lock file never covers a module.

    dsoxlab start modules-module-registry

    45m intermediate shell Companion guide (French)

  • Publish and consume module versions with Git tags

    Publish three versions of a shared module, then consume them from three projects: an immutable reference, a minor tag and a major tag, with no version argument in sight.

    dsoxlab start modules-version-modules

    45m intermediate shell Companion guide (French)

  • Prove a module with terraform test, mutations included

    Write the .tftest.hcl suite of a module, covering its default, its options and its refusal of an invalid input: every behaviour is checked by mutating the module.

    dsoxlab start modules-test-module

    45m intermediate shell Companion guide (French)

  • Make a module composable, and prove it from the plan JSON

    Strip a legacy module of its own provider configuration, invert its dependency and document it, then read the proof in provider_config and module_calls.

    dsoxlab start modules-module-best-practices

    45m intermediate shell Companion guide (French)

  • Refactor a copy-pasted project without destroying anything

    Extract two copy-pasted resources into a typed module called once, and prove from the state identifiers that nothing was destroyed on the way.

    dsoxlab start modules-module-anti-patterns

    45m intermediate shell Companion guide (French)

Environments

  • Which value wins, and how to prove it

    Serve three environments from one configuration, restore the guard rail a stray terraform.tfvars had silenced, and prove which source wins from the plan JSON.

    dsoxlab start environments-per-environment-variables

    45m advanced shell Companion guide (French)

  • One directory, three states that never see each other

    Drive three state instances from a single configuration through terraform.workspace, then retire an inherited workspace without leaving an orphan behind.

    dsoxlab start environments-workspace

    45m advanced shell Companion guide (French)

Terraform on AWS, via Floci

  • AWS provider: authentication, endpoints and default tags

    Configure a pinned AWS provider so it authenticates against a local API emulator, then prove the instance really runs.

    dsoxlab start aws-provider-aws-first-ec2

    40m intermediate shell Companion guide (French)

  • Security group: dedicated rules, for_each and a deterministic subnet

    Build a security group whose rules are all dedicated resources, factor the ingress rules with for_each, and place an instance in a subnet designated by its tag.

    dsoxlab start aws-sg-subnet-instance

    45m intermediate shell Companion guide (French)

Associate certification (004)

  • The commands the exam expects, done rather than recited

    Eight gestures a candidate must be able to perform: `validate`, `fmt`, the precedence cascade, `moved`, `import`, `removed` without destroying, `-replace`, and what `sensitive` really protects. Exit codes are recorded as they happen: they cannot be reconstructed afterwards.

    dsoxlab start certifications-associate-essential-commands

    30m advanced shell Companion guide (French)

  • Associate 004: mock exam

    Associate 004 mock exam, validated by pytest.

    dsoxlab start certifications-associate-mock-004

    4h advanced shell Companion guide (French)

Back to all 352 labs How to run a lab