Terraform Pro 002 cheat sheet — every exam topic on one page
196 key facts across 7 exam domains — every topic on the Terraform Pro 002 blueprint, with the exam pattern behind each. A condensed cheat sheet, distilled from the full Terraform Pro 002 revision notes. Skim it the week of your exam.
Updated
Scalable Terraform configuration design
20% of the examTerraform Professional (002): Turning Requirements into Predictable, Secure Configuration
- Read requirements for timing, failure mode and persistence — those words decide which Terraform mechanism fits
- No default means required; nullable = false rejects null on required inputs and makes null fall back to the default on optional ones
- optional(type, default) fills omitted and null object attributes; a variable default is replaced wholesale by any caller value and never merges
- Variable defaults must be literals: derive conventions in locals (coalesce for overridable values) — modules cannot read their own outputs
- Environment differences are explicit inputs set by each root module, never workspace-name or environment-name checks inside the module
- Key for_each by stable, unique identifiers known at plan time; apply-time IDs are values, not keys; fixed timestamps belong in state (time_static)
- Validation (1.9+ may reference other variables), preconditions and postconditions error; check blocks and their scoped data sources only warn; postconditions never roll back
- sensitive redacts display and propagates, but values remain in state and saved plans; outputs built from sensitive values must be marked sensitive
- Ephemeral values and write-only arguments are never persisted; root outputs cannot be ephemeral; bump the _wo_version argument to send a new write-only value
- Commit .terraform.lock.hcl (lock every platform with terraform providers lock -platform), use >= required_version in shared modules, and pair prevent_destroy with provider deletion protection
How the exam tests this
Expect requirement-driven scenarios where several options are real Terraform features and the stem's constraints decide: whether a rule must stop the run or only warn, fire before planning or after apply, live on the input, the consuming resource or the output, and whether a secret may be stored in state or a saved plan. Input-design items show variable declarations and ask what a caller's value becomes — omitted versus null, optional attributes with and without defaults, defaults that do not merge, nullable. Others ask why a configuration fails (sensitive values in outputs or for_each, ephemeral root outputs, defaults referencing variables, unknown for_each keys) or what changes after an apply (failed postconditions, write-only arguments that show no diff). Multi-select items pair two facts, such as display versus storage of sensitive values or a plan-time guard plus an object-level guard. Version and lock-file items cover reproducible provider installs across platforms and minimum CLI versions for shared modules.
Terraform Professional: Tracing Variables, Locals, Outputs and for_each
- Last loaded wins: TF_VAR_ < terraform.tfvars < *.auto.tfvars (by filename) < -var/-var-file in order; the default is only a fallback.
- Undeclared names: TF_VAR_ is silently ignored, a .tfvars file only warns, -var is an error — and every one of these sources sets root variables only.
- Object conversion silently drops undeclared attributes; optional(type, default) fills attributes that are omitted or null.
- A nullable variable passed null gets null; nullable = false makes null take the default, or fail when there is no default.
- Locals are module-scoped across files, referenced as local.name, and never visible to child or parent modules; values go down through arguments and up through outputs.
- terraform output shows root outputs only; terraform console evaluates any expression, and -scope evaluates inside a child module instance.
- Sensitivity follows the value through functions, locals and module outputs: mark root outputs sensitive, and unmark only what becomes a for_each key.
- for_each takes a map or a set of strings with keys known at plan; instances are addressed by key, form a map (no bare splat), and renaming a key is destroy and create.
How the exam tests this
Expect short configurations in the stem and a question about the result: which source supplies a variable's value, what an undeclared name does from each source, what an object-typed variable contains after conversion, or what nullable = false does with and without a default. Tracing items show a wrong name or value in a plan and ask where it originates — a module argument, a swapped wiring, an output that echoes its input, a local that is never used, a case mismatch in a validation rule. for_each items ask for the expression that fixes an invalid, unknown-key or sensitive for_each error, the address of an instance (including inside a module created with for_each), how to reference a for_each collection, and what the plan proposes after a key rename, an element removal under count, or a key collision. Several items add a constraint — keep secrets redacted, fix every caller, keep addresses stable — that rules out a fix which would otherwise work.
Terraform Professional (002): functions, expressions, dynamic blocks and custom conditions
- merge() lets the last argument win and is shallow: pass values that must not be overridden last, and merge nested maps explicitly.
- lookup() defaults a missing key, try()/can() catch a missing attribute path, coalesce() skips null and empty strings.
- Map keys and sets of strings iterate in lexical order; sort() converts numbers to strings and sorts them as text.
- element() wraps around, list[i] does not, and slice() excludes its end index and always returns a list.
- A for expression that produces duplicate map keys fails unless you add ... to group values into lists.
- replace() is literal unless the pattern is wrapped in /slashes/; cidrsubnets() aligns each range to its own size.
- optional(type, default) substitutes the default for omitted or null attributes, applies nested defaults top-down and drops undeclared attributes.
- Dynamic blocks use the block label (or iterator) not each, cannot generate lifecycle or provisioner, and use cond ? [] : [x] for an optional block.
- Preconditions and postconditions stop the run (only postconditions can use self); check block assertions only warn.
- templatefile() sees only the variables you pass it; provider functions use provider::<local name>::<fn> and need Terraform 1.8+ plus a provider version that ships the function.
How the exam tests this
Expect short configurations with a function call or for expression and a request to predict the exact result, so precision on precedence, ordering, inclusive and exclusive bounds, wrap-around, regex versus literal matching, CIDR alignment and result types decides the answer. Scenario items state a requirement (mandatory values must win, no block when a value is null, stop before creation, report without blocking, valid JSON) and ask which construct, iterator reference, optional() default or condition type meets it. Some items ask you to select two correct expressions or statements.
Terraform Professional (002): configuring providers and the Terraform MCP server
- A resource or data block without a provider argument uses the unaliased default configuration; aliases are selected with provider = <local name>.<alias>.
- If every block for a provider is aliased, Terraform creates an implied empty default configuration that unaliased resources and child modules use.
- Child modules inherit default configurations only; aliased ones are never inherited, and setting providers cancels default inheritance, so list everything the child needs.
- Modules that need alternate configurations declare configuration_aliases; callers map them, one level at a time, and one configuration can fill several names.
- Modules with their own provider blocks cannot use count, for_each or depends_on, and any provider configuration must stay until its resources are destroyed.
- Every module declares its own required_providers; an undeclared local name falls back to the hashicorp namespace.
- Provider arguments must be known during plan: split configurations when they depend on computed attributes, and read the other side with a data source.
- The Terraform MCP server's registry toolset needs no token; registry-private and terraform need a valid TFE_TOKEN (plus TFE_ADDRESS for Terraform Enterprise).
- Omitting --toolsets loads all (token-gated toolsets register only with a valid TFE_TOKEN); the default keyword selects the registry toolset alone.
- ENABLE_TF_OPERATIONS (default false) gates destructive tools; the token's permissions bound everything; the hosted remote server reaches US HCP Terraform organizations only and is never destructive.
How the exam tests this
Expect short HCL snippets with provider blocks, module calls and required_providers entries, asking which configuration a resource or module uses, why plan, validate or init fails, or which providers map or configuration_aliases line fixes it. Scenario items add constraints such as no provider blocks in the module, root resources unchanged, least configuration, no manual steps, an air-gapped network or the resources must really be destroyed. MCP server items ask which toolset, token, environment variable, deployment option or prompt meets a requirement, often combining a capability with a safety limit. Some items ask you to select two.
Terraform state and backend operations
13% of the examTerraform Professional: Remote Backends and Remote State
- Backend blocks accept literals only — no variables, locals, data sources or terraform.workspace — and a configuration has one backend, never alongside a cloud block.
- Vary backend values per environment with -backend-config files or KEY=VALUE pairs at init; later options override earlier ones and the block. Only init accepts -backend-config.
- Backend configuration values, credentials included, persist in .terraform/terraform.tfstate and in saved plans, so supply credentials through environment variables or roles.
- After a backend change, -migrate-state copies state and -reconfigure starts empty; -force-copy answers the prompts (and implies -migrate-state), and -input=false without it aborts.
- S3 locking is use_lockfile (off by default): a .tflock object at the state key that needs Get, Put and Delete permissions. Keep dynamodb_table alongside it until every job runs the new config.
- Versioning recovers state, a customer managed KMS key needs Encrypt, Decrypt and GenerateDataKey, and only an access control such as a bucket-policy deny keeps secrets in state from other readers.
- Non-default S3 workspaces live at <workspace_key_prefix>/<workspace>/<key> (prefix env:), and so do their lock files.
- The cloud block maps to one workspace by name or many by tags (never both); TF_CLOUD_* and TF_WORKSPACE fill only omitted attributes, and an empty cloud {} is still required.
- terraform_remote_state reads root outputs only, defaults to the default workspace and needs full state access; HCP sharing is set on the producer, while tfe_outputs is governed by its token.
How the exam tests this
Expect production scenarios in which a backend or remote-state setup fails or must change, often with a short backend, cloud or data-source block in the stem. Items ask what init does with a named value in a backend block, how to supply per-environment values with one committed block, which -backend-config value wins, where backend credentials persist, and which init options copy state in a pipeline without a terminal — or why a plan suddenly wants to recreate everything. S3 items cover use_lockfile permissions and lock object paths, a safe cutover from DynamoDB locking, versioning versus encryption versus access control, KMS permissions and the key layout of non-default workspaces under a prefix-scoped IAM role. HCP Terraform items cover name versus tags in the cloud block, environment-variable overrides, TF_WORKSPACE in non-interactive jobs, remote execution and credentials, migrating several workspaces, remote state sharing scopes and tfe_outputs. Scenarios typically include an operational requirement, so the skill tested is choosing the mechanism that meets it with the least change and the least exposure, not just one that works.
Terraform Professional: Inspecting State and Remediating Drift
- state list prints addresses (filter by prefix or -id), state show prints one instance's attributes, show takes a file path not an address, and output reads root outputs only.
- for_each keys are quoted strings attached to the segment that repeats; wrap the whole address in single quotes on Unix shells.
- show -json, output -json and output -raw print sensitive values in plain text; sensitive_values in the JSON marks what to strip.
- terraform console evaluates expressions against state and holds a state lock while open; exit it instead of force-unlocking.
- state pull is the backup; state push refuses a different lineage or a higher remote serial, and -force overwrites without merging.
- apply -refresh-only accepts drift into state only; update the configuration too, or the next normal apply reverts it or recreates a deleted object.
- A normal plan and apply reverts drift; -refresh=false and state-only views cannot see it.
- removed blocks (destroy = false) forget objects in a reviewed plan but cannot target one instance key; state rm needs the block deleted as well.
- moved blocks go from old to new address; count to for_each needs one per instance. Prefer -replace over taint, and untaint a healthy object tainted by a failed provisioner.
- Health assessments need remote or agent execution and a successful latest run; org enforcement overrides workspaces; fix drift by plan and apply or by changing configuration.
How the exam tests this
Expect production scenarios built around a constraint: read-only during a freeze, no state CLI commands, every change in a reviewed plan, keep the object running, fewest steps, or secrets that must stay out of logs. Inspection items give a goal (an inventory of one module, the address behind a cloud ID, one instance's attributes, an expression to compute, a restorable backup) and offer commands that take the wrong kind of argument, sometimes with a short for_each block and quoted addresses to build. Drift items describe a console change, deletion or autoscaler and ask what the next plan proposes, how to accept or revert it, which pipeline flags hide it, and how expected drift is tolerated per resource or across a provider. State-surgery items cover removed versus state rm, moved block direction and count-to-for_each moves, state mv with -dry-run, -replace versus taint and untaint after a failed provisioner, state push lineage and serial refusals, and replace-provider argument order. HCP Terraform items cover health assessment enforcement, eligibility and the two remediation paths.
Terraform Professional: Dependency Chains and Version Upgrades
- Only installing a different binary changes the Terraform release; required_version checks it, the lock file tracks providers only, and init -upgrade moves providers and modules.
- The running CLI must satisfy every required_version in the module tree; an unmet one, even in a nested or registry module, stops Terraform before it acts.
- ~> lets only the right-most stated component grow: ~> 1.12.0 means patches only, ~> 1.12 allows any later 1.x. Pre-releases match only exact constraints.
- Reusable modules declare a minimum (>=) at the latest feature they use; root modules bound both ends.
- terraform providers shows which module declares each provider constraint, terraform modules -json lists every module call, terraform graph shows ordering, and state pull's terraform_version shows what last wrote state.
- terraform graph only knows references and depends_on; add depends_on for run-time-only dependencies, but prefer references over module-level depends_on.
- Upgrade terraform_remote_state readers no later than the producer, and publish renamed outputs alongside the old name until consumers switch.
- A saved plan applies only with the same Terraform release and the same lock selections, so pin one exact release for plan and apply and use plain init in both.
- Roll dev to stage to prod, core and providers in separate changes; plan never persists state, and a direct jump between v1.x releases is supported.
- Downgrades are not guaranteed: snapshot state before the first apply and roll back by restoring it (state push -force) with the old version.
How the exam tests this
Expect production scenarios with a stated constraint: no long-lived branches, independent consumer schedules, keep a module on a supported release, least ongoing effort, or avoid behaviour HashiCorp does not guarantee. Items present a version constraint or a terraform block and ask whether a given CLI release runs, which constraint a module or root should declare, or what minimum a set of language features requires. Others give an inventory goal and ask which command or state field answers it, a pipeline failure involving saved plans, lock selections or image tags and ask for the cause, or a failed upgrade and ask for the supported rollback. Several are multiple-response and some distractors use the same field name or a real command that answers a different question.
Infrastructure lifecycle and operations
22% of the examTerraform Professional: Scaling Projects and Multiple Environments
- CLI workspaces share one configuration, backend and credentials; they fit temporary parallel copies, not environments that need separate credentials, access controls or state storage.
- workspace select fails on a missing workspace unless -or-create is set; workspace new fails on an existing one and can seed state with -state.
- workspace delete refuses the current workspace and one still tracking resources; -force leaves real objects unmanaged, and default can never be deleted.
- terraform.workspace is default when nothing else is selected; indexing a map with an unknown workspace fails, so use lookup() with a fallback.
- TF_WORKSPACE is for non-interactive use, and while it is set, workspace select refuses to switch.
- A root per environment gives each its own backend, credentials, constraints, lock file and module pins, so changes are promoted by bumping each root in turn.
- Switch a shared directory between environments with init -reconfigure, never -migrate-state; give concurrent jobs separate TF_DATA_DIR values.
- Scale by splitting roots along rate of change and ownership; -target is for exceptional use and hides drift.
- Lower -parallelism (default 10) for API rate limits; set it per command with TF_CLI_ARGS_plan/apply, which also replaces TFE_PARALLELISM on agents.
- Stacks: components in .tfcomponent.hcl, one deployment per environment in .tfdeploy.hcl; ungrouped deployments get <name>_default with no auto-approval; retire with destroy = true.
How the exam tests this
Expect production scenarios that state an isolation, ownership or automation constraint and ask which environment layout or command meets it with the least restructuring: separate credentials or state storage versus a temporary test copy, a per-pull-request workspace that must be created once and reused, a cleanup job whose workspace delete is refused, a pipeline that must target a workspace without relying on an earlier select, a shared runner whose parallel jobs read the wrong state, or a module release that must reach dev before prod. Short HCL snippets in the stem test terraform.workspace interpolation and map indexing, Stacks deployment groups and auto-approve checks, and module wiring that fails on a first plan. Other items cover when to split a monolithic configuration, what routine -target hides, setting -parallelism through environment variables in a template you cannot edit or on HCP Terraform agents, retiring a Stack deployment, and HCP Terraform project scoping, priority variable sets, tag inheritance and project deletion. Several are Select TWO.
Terraform Professional: Planning and Applying Infrastructure Changes
- terraform apply FILE executes exactly the saved plan: no approval prompt, recorded variable values (sensitive ones included), no re-planning and no planning options such as -var, -target or -refresh=false.
- Run options such as -parallelism, -lock-timeout and -input=false are accepted with a saved plan because they change how it runs, not what it contains.
- Any write to state after planning makes a saved plan stale; the only fix is a new plan and a new review, never refresh, state rollback or waiting on the lock.
- Hand plans between jobs with the whole initialised working directory (.terraform and the lock file), the same Terraform and provider versions and the same OS/architecture; never init -upgrade before applying.
- Plan files and their JSON rendering hold sensitive values in cleartext; restrict and expire them like secrets.
- With -detailed-exitcode, 0 = no changes, 1 = error, 2 = changes; scripts must branch on 1 and 2 separately.
- Policy gates test whether change.actions contains "delete" (covering both replace encodings), check after_unknown before reading after, and never rely on action_reason.
- -target includes dependencies, never dependents, is for exceptional cases only, and should be followed by a full plan.
- Apply is not transactional: completed changes stay in state, failed creates are tainted, the first Ctrl-C stops gracefully, and recovery is a fresh reviewed plan.
- In HCP Terraform, plain CLI plans and pull-request plans are speculative; saved cloud plans (CLI 1.6+) and CLI applies work only in workspaces without a linked VCS repository.
How the exam tests this
Expect production scenarios decided by a stated constraint: the apply must match the reviewed plan exactly, no concurrent state writes, a plan file must move between CI runners, secrets must not leak, or the workspace is VCS-driven. Many items show a plan excerpt, a JSON field or a short shell script and ask what Terraform will do, which flag fits, or which option is accepted with a saved plan. Wrong options are usually real flags and commands that solve a neighbouring problem, so know what each one does and does not do; some items ask you to select two correct statements.
Terraform Professional: Importing Infrastructure and Managing Its Lifecycle
- Import blocks change state only when their reviewed plan is applied; terraform import writes state immediately with no plan, which can leave shared state ahead of the merged code.
- The to address must match a declared managed resource instance exactly: module.<call>.<type>.<name>, [n] for count, ["key"] for for_each, and the key on whichever block has for_each. Data sources cannot be imported.
- A for_each import block must reproduce the resource's own instance keys; iterating the same map as the resource is the simplest way.
- The import id must be known at plan time: variables, locals and data sources work; attributes of resources created in the same run do not. id and identity are mutually exclusive.
- A safe import plan is import-only. An extra update or replacement means the resource block differs from reality: fix the block, not the lifecycle settings.
- terraform plan -generate-config-out writes resource blocks with literal values to a new file only; the import block's provider argument is allowed only when generating configuration.
- terraform query with list blocks in .tfquery.hcl discovers unknown objects; limit defaults to 100 and include_resource adds attributes; -generate-config-out writes import and resource blocks.
- create_before_destroy, prevent_destroy and ignore_changes take literal values only; for per-environment settings use two count-toggled resource blocks. replace_triggered_by takes managed-resource references, and conditions take expressions.
- create_before_destroy needs unique-name handling, spreads to the resources it depends on and stops destroy provisioners; replace_triggered_by takes managed resources or attributes, with terraform_data for plain values and each.key for per-instance scope.
- action_trigger runs provider actions on create and update events, drift fixes included; -invoke runs one action alone; moved blocks refactor module calls, and a shared module ships its own moves.
How the exam tests this
Expect production scenarios decided by a stated constraint: imports must appear in a reviewed plan before state changes, nothing may be destroyed or recreated, the team must not own another team's object, state commands are forbidden, callers may only bump a module version, or protection must apply in production only. Many items show a short block of HCL and ask for the exact import address, id expression, lifecycle content or moved block, so practise writing addresses for modules, count and for_each. Be clear on how neighbouring features differ: removed versus moved blocks, data sources versus imports, depends_on versus replace_triggered_by, and plan versus query configuration generation. Some items ask you to select two correct statements.
Terraform Professional: Troubleshooting Deployment Issues with Logs, Validation, Locks and State Recovery
- Classify the error first: configuration, initialization, state, platform API or provider plugin each have a different fix.
- TF_LOG turns logging on and sets the level; TF_LOG_PATH only redirects it to a file and does nothing alone. TF_LOG=JSON is TRACE in JSON and its format is not stable.
- TF_LOG_PROVIDER isolates plugin and API traffic; TF_LOG_CORE isolates Terraform's own graph and evaluation work.
- For HCP Terraform remote runs, set TF_LOG as a workspace environment variable, leave TF_LOG_PATH unset, and remember Sensitive hides the value, not the trace output.
- terraform init -backend=false then terraform validate checks code offline; it never calls provider APIs, so platform errors appear only at plan or apply. Use -json for CI annotations.
- A locked version outside a new constraint, or a bad release you excluded with !=, needs terraform init -upgrade; commit the lock file and never hand-edit it.
- Lock files built through a mirror hold hashes for one platform; run terraform providers lock -platform=... for every OS and architecture and commit.
- depends_on never breaks a cycle; restructure references, and use terraform graph -type=plan -draw-cycles to see large ones.
- Stale lock: confirm nothing runs, force-unlock, then plan. Failed state save: terraform state push errored.tfstate.
- Remove resources before their provider configuration; apply force_destroy before destroy; untaint a healthy object a timed-out create left tainted; import an 'already exists' object only when no other state owns it.
How the exam tests this
Expect production scenarios built around a quoted error message, a short log or lock file excerpt, or a pipeline description, with one stated constraint deciding the answer: the CI runner has no credentials, checksum verification must stay on, the lock file must stay authoritative, the existing object must keep running, or the recovery must take the fewest operations. The skills tested are classifying an error by layer, knowing exactly what each logging variable, init option and recovery command does and does not change, ordering removals and destroy-time changes correctly, and knowing what terraform validate can and cannot detect. Several items ask you to select two statements.
Terraform Professional: Sharing Data Between Projects and Managing Provider and Module Versions
- terraform output -raw handles only strings, numbers and booleans; use -json (with jq) for lists, maps and objects
- terraform output -json without a name returns {name: {sensitive, type, value}}; with a name it returns the bare value
- sensitive = true hides values in plan, apply and the unnamed output listing only; state, named output, -json and -raw expose them in plain text
- terraform_remote_state exposes only root outputs but requires read access to the whole state snapshot, and copies what it reads into the consumer's state
- Child module values reach other projects only through a root output, and only after an apply records it in state
- Publishing values to SSM, Consul, a ConfigMap or DNS grants access per value; direct data source lookups remove state coupling but must match exactly one object
- In HCP Terraform use tfe_outputs (values is sensitive wholesale; nonsensitive_values is the safe subset) and run triggers, which fire on a successful apply and share no data
- The external data source needs a JSON object of strings, reports errors via stderr and a non-zero exit, and runs on every plan, so it must have no side effects
- Terraform combines every module's provider constraints and needs one version satisfying all; reusable modules set minimums, root modules use ~>, pre-releases need an exact version
- The lock file pins providers only: commit it, upgrade with init -upgrade where the change is made, add platforms with terraform providers lock -platform, and pin modules exactly (Git by commit SHA)
How the exam tests this
Expect production scenarios decided by one stated constraint: consumers may not read the producer's state, the solution must survive a project split or backend migration, a value must stay visible in plans while a secret stays redacted, every upgrade must be reviewed, or checksum verification must stay on across operating systems. Items often show a short terraform output or jq command, a version constraint with a list of available releases, or a module source string and ask what Terraform does or which variant works. Wrong options are real commands, flags and data sources that solve a neighbouring problem, and several items ask you to select two statements.
CI/CD and automation workflows
10% of the examTerraform Professional: CI/CD Pipelines and Deployment Strategies
- terraform fmt -check -recursive is the read-only formatting gate; add -diff for useful logs. Without -recursive, subdirectories are skipped; without -check, fmt rewrites files.
- The plugin cache is not concurrency safe and is used only for packages that match lock-file checksums; offline runners need -plugin-dir or a filesystem_mirror-only provider_installation.
- setup-terraform's default wrapper exposes stdout, stderr and exitcode outputs but corrupts piped output; set terraform_wrapper: false for redirection.
- TF_TOKEN_<host> encodes periods as _ and hyphens as __, and beats credentials written by terraform login.
- VCS triggers: always (any file), specified paths (globs or prefixes, prefixes need a working directory, include local modules), or Git tags (every matching tag, regardless of branch).
- API-driven configuration versions: archive the directory's contents; auto-queue-runs defaults to true; speculative versions can never be applied.
- Run tasks block only when mandatory, and timeouts count as failures; inspect changes at post-plan or pre-apply.
- Auto-apply covers UI, API and VCS runs, not CLI runs (-auto-approve); auto-approve only non-critical environments and keep one outstanding plan per state.
- Blue/green and canary: weights move traffic instantly at the load balancer, toggles move capacity; create, verify, shift, then decommission. DNS weights wait on TTLs, forward-action stickiness delays returning users.
- Promote exact, tested artifacts (exact module versions, pinned image IDs or HCP Packer channels) and roll back by applying the last good configuration; HCP state rollback changes state only.
How the exam tests this
Expect production scenarios decided by one stated constraint: the job must stay read-only, runners share a host or have no internet, output is piped to another tool, a run must be impossible to apply, prod must deploy only on a tag or only a tested artifact, traffic must shift without DNS delay, or green must stay up for debugging. Some items show a short HCL block, a pipeline expression or a command and ask what happens or what to change. Wrong options are usually real flags and settings that solve a neighbouring problem (for example two configuration-version attributes, two enforcement levels or two ways of caching providers), and several items ask you to select two correct statements.
Terraform Professional (002): Dynamic Provider Credentials and Variable Files
- AWS dynamic credentials need TFC_AWS_PROVIDER_AUTH=true and a role ARN, set as environment variables; no STS call at all means the switch is missing, a denied call means the trust policy is wrong
- The sub claim is organization:ORG:project:PROJECT:workspace:WORKSPACE:run_phase:PHASE; wildcard it only with StringLike and always check aud and the organization
- Renaming or moving an organization, project or workspace changes sub (the ID claims do not); a custom AWS audience must be added to both the OIDC provider and the trust policy
- PLAN and APPLY role variables beat the RUN role, which is only a fallback; each role must trust its own run phase
- Azure federated credentials match exactly (one per workspace per phase); Google Cloud uses assertion.sub conditions and the numeric project number
- Tagged configurations need their own PROVIDER_AUTH_<TAG>=true, live under aliases["<TAG>"], and force you to map every provider, the default one included; TFC_DEFAULT_ values only fill gaps
- Workspace variables override variable sets; more specific sets beat global sets; priority sets beat everything, including -var
- Vault-backed credentials need Vault dynamic credentials first, then TFC_VAULT_BACKED_ variables, with secrets-engine reads removed from code
- On the command line, the last -var or -var-file wins; auto files load from the root directory only, in name order, above terraform.tfvars(.json) and TF_VAR_
- Maps and objects are never merged across variable sources; combine separate variables with merge() instead
How the exam tests this
Expect production scenarios built around a stated constraint — least effort, no long-lived secrets, every future workspace in a project but none outside it, teams sharing a project, a workspace that must stay where it is — with options that are all real variables, claims or files. Items give a set of TFC_ environment variables or a trust condition and ask which role each run phase assumes, why plans work but applies fail, what broke after a rename, move or audience change, or which provider argument maps a tagged configuration. Azure, Google Cloud and Vault-backed items test the integration-specific rules. Variable-file items show command lines, file listings or HCL and ask which value wins, including JSON and auto files, subdirectories, map and object replacement, VCS-driven versus CLI-driven runs, priority variable sets and Terraform Enterprise. Several are Select TWO, pairing the HCP Terraform side with the cloud side.
Terraform security and governance
13% of the examTerraform Professional (002): Secrets, Rotation and Pipeline Identities
- sensitive = true redacts output; it never keeps a value out of state — only ephemeral sources feeding write-only arguments do
- CLI-driven runs upload the directory filtered by .terraformignore (not .gitignore) and load uploaded *.auto.tfvars files
- Sensitive workspace variables are write-only for everyone: overwrite to rotate, delete and recreate to change the key or flag
- Ephemeral values are allowed only in write-only arguments, provider blocks, locals, ephemeral blocks, ephemeral variables/outputs and provisioners; anywhere else the plan fails
- Write-only values are never diffed: bump the _wo_version argument to push a rotated secret
- manage_master_user_password lets RDS own and rotate the password in Secrets Manager; it conflicts with password and password_wo, and master_user_secret_kms_key_id encrypts the secret
- With Vault dynamic provider credentials, drop token/address from the provider, set skip_child_token = true, use renewable short-TTL tokens, and use ephemeral or Vault-backed credentials rather than secret-generating data sources
- Trust policies must check aud and a sub that includes the organization; use StringLike for wildcards and pin each of the plan and apply roles to its own run_phase
- GitHub OIDC needs id-token: write; an environment in the job changes sub to environment:NAME
- Every fixed workspace permission set reads full state — use custom Read outputs only and tfe_outputs to share outputs safely; team tokens for runs, the organization token for setup
How the exam tests this
Expect production scenarios with a stated constraint — no copy of the secret in state, rotation without a Terraform run, no long-lived tokens, least privilege, no downtime during rotation — where every option is a real argument, variable, claim, token type or permission set that fails only on that constraint. Items show HCL or trust-policy snippets and ask what plan does or what to change, and multi-select items ask which statements about a mechanism are true. Near-twins appear side by side: secret_string versus secret_string_wo, kms_key_id versus master_user_secret_kms_key_id, StringEquals versus StringLike, custom Read versus Read outputs only, the ref, pull_request and environment forms of a GitHub sub claim, and team versus organization tokens. Harder items combine two facts, such as what a CLI-driven run uploads, or why a split plan role still fails without per-phase trust.
Terraform Professional (002): Policy Rules, Overrides and Risky Changes
- Organization baselines go in a globally scoped policy set; team rules go in extra sets scoped to a project, workspace or tag.
- Every policy set in scope is evaluated independently, so a team's own set can never relax a global rule; exemptions are exclusions on the set that fails.
- Exclude the unit that matches the exempt group: a workspace, a project, or a tag that cuts across projects.
- Sentinel levels are advisory, soft-mandatory and hard-mandatory; OPA has advisory (the default) and mandatory.
- Manage policy overrides lets a team continue overridable failures run by run; Manage policies is for authoring and does not grant overrides.
- A hard-mandatory policy-check failure has no override: fix the code or the policy and queue a new run.
- Policy evaluations (Agent-type Sentinel, all OPA) allow runtime pinning and a set-level override option but cannot read cost estimates.
- Policies run after a successful plan and before apply; an errored plan never reaches them and never applies.
- Test change.actions contains "delete" to catch deletes and both replacement orders; tfrun.is_destroy only sees destroy plans.
- prevent_destroy disappears with its resource block; pair a mandatory delete policy with provider deletion protection, and split workspaces to shrink the blast radius.
How the exam tests this
Expect production governance scenarios with a stated constraint — cover future workspaces, exempt one group without losing coverage, block but allow approved exceptions, keep one copy of policy code, no surprise blocking — where every option is a real scope, exclusion, enforcement level, permission, import or setting that fails only on that constraint. Items show sentinel.hcl, policies.hcl, Sentinel or Rego snippets and ask why a run continued or failed, or which condition to use. Near-twins appear side by side: soft-mandatory versus hard-mandatory versus OPA mandatory, Manage policies versus Manage policy overrides, policy checks versus policy evaluations, project versus workspace versus tag scope, resource_changes versus planned_values, the two replacement action orders, and tfrun.is_destroy versus delete actions in the plan. Harder items combine two facts, such as two failing sets with different override rules, or which protections each cover removed blocks, replacements and destroy runs.
Advanced module usage and registry practices
10% of the examTerraform Professional (002): Refactoring into Modules and the Module Contract
- Write a module when resources are used together and repeated, or when standards must be enforced; avoid thin wrappers around one resource type.
- Reuse across repositories with independent upgrades needs versioned releases that callers pin, not a local path or a branch reference.
- Scope modules by encapsulation, privileges and volatility; prefer small composable modules over one module full of create_* flags.
- Pass dependencies in as inputs; use data-only modules to hide how shared values are retrieved.
- moved blocks go in the module that can see both addresses, carry instance keys where for_each lives, and let a shared module refactor itself for every caller.
- The contract is inputs, outputs, provider requirements and resource addresses; locals and internal wiring are private, and narrow outputs keep the contract small.
- Required inputs have no default; optional() defaults replace both omitted and null attributes; specific types catch caller mistakes on entry.
- Modules declare required_providers with a source and a >= minimum and contain no provider blocks.
- Breaking changes (new required inputs, type changes, removed outputs or moved blocks) need a major version; Terraform 1.15 deprecated warns without failing.
How the exam tests this
Expect production scenarios with a stated constraint — callers may only bump the version, no resource may be recreated, each team upgrades on its own schedule, the release must be minor — where every option is a real Terraform construct that fails only on that constraint. Refactoring items show for_each resources, module calls or nested-module splits and ask which moved blocks are needed and where they go. Contract items ask which change is safe without caller edits, what version number a release needs, how callers should receive a value, or what deprecation shows to which caller. Scoping items weigh encapsulation, privileges and volatility against each other, or compare thin wrappers, flag-driven catch-all modules and composed small modules. Provider items cover required_providers sources and minimum versions, legacy provider blocks and lock files. Multi-select items pair two facts, such as two breaking changes or two accurate statements about Terraform 1.15 deprecation.
Terraform Professional (002): Registry Modules and terraform test
- Public registry addresses are NAMESPACE/NAME/PROVIDER; private ones add the hostname: HOSTNAME/ORG/NAME/PROVIDER; // selects a nested module
- version works only with registry sources, and any change to source or version needs terraform init; the lock file covers providers, not modules
- On Terraform 1.15, module source and version can use const = true variables and locals built only from them
- Private registry credentials come from CLI credentials blocks or TF_TOKEN_ variables (periods as _, hyphens as __), using user or team tokens — never organization tokens
- Registry docs are per version: match the docs to the provider or module version you actually run
- Public registry: public GitHub repo named terraform-<PROVIDER>-<NAME>, standard structure, x.y.z tags with optional v; private monorepos need a Source Directory and a tag prefix per module
- Deleting a Git tag does not remove a registry version — delete it in the registry
- A run defaults to command = apply and creates real infrastructure, destroyed in reverse run order when the file ends; plan runs can only assert on known values
- expect_failures covers custom conditions only, in the run’s own command; test-file variables beat -var and tfvars, run-level beats file-level
- Mocks generate computed values during apply unless override_during = plan; parallel runs need parallel = true, no cross-references and separate state (state_key)
How the exam tests this
Expect production scenarios with a stated constraint — a non-interactive CI job, no real infrastructure, keep command = plan, stay on the current major version, release one module of a monorepo, no long-lived cloud keys — where every option is a real address form, command, flag, block or setting that fails only on that constraint. Items show HCL module blocks or test files and ask what init, plan or terraform test does, or what to change. Near-twins sit side by side: swapped address parts, // versus / versus ?ref=, -filter versus -test-directory, -run-parallelism versus -parallelism, run.<name> versus module.<name>, mock_resource versus mock_data, same versus different state_key, and TF_TOKEN_ name encodings. Multi-select items ask which tags become versions, which credentials work, or which conditions block parallel runs; harder items combine two facts, such as variable precedence across test-file and tfvars scopes, or why expect_failures fails in an apply run.
HCP Terraform and Terraform Enterprise operations
12% of the examTerraform Professional (002): Operating HCP Terraform and Tracking Usage and Cost
- Agent execution = private network with the full HCP Terraform run pipeline; agents connect outbound only.
- Local execution keeps state storage, locking and sharing but drops variables, variable sets, notifications, health assessments, policies and run tasks.
- Scoping an agent pool doesn't detach existing users; rotate agent tokens make-before-break; any agent may run any phase.
- CLI runs ignore workspace auto-apply (use -auto-approve), cannot apply in VCS-linked workspaces, and Ctrl-C after apply starts stops only the logs.
- Every fixed role reads full state; only a custom role with Read outputs only hides secrets. Project Maintain administers workspaces; project Admin administers the project.
- A lock blocks applies and uploads by others; deleting a workspace never destroys its infrastructure.
- Generic webhooks: set a token, verify X-TFE-Notification-Signature (HMAC SHA-512); enabling needs a 2xx test response.
- Continuous validation needs Terraform 1.3+ and checks or conditions; assessments never change infrastructure and yield to runs.
- HCP Terraform bills the hourly peak of managed resources (workspaces + Stacks, minus null_resource and terraform_data); partial hours count in full.
- Cost estimation = cloud spend, off by default, skipped for -target; tfrun.cost_estimate prior/proposed are before/after monthly totals (decimal strings), delta = proposed minus prior.
How the exam tests this
Expect operational scenarios where every option is a real HCP Terraform feature and one stated constraint decides: a network that blocks inbound traffic plus mandatory governance, a least-privilege team with a narrow job, a notification endpoint that misbehaves, a lock someone else holds, or a sandbox that must clean itself up. Execution-mode items ask which features survive a switch to local mode. Agent items cover pool scoping, token rotation and pools that span networks. CLI items cover auto-apply versus -auto-approve, Ctrl-C and VCS-linked workspaces. Billing items give resource counts across an hour, a mix of resource types or a Stack, and ask what HCP Terraform bills; cost items separate cloud spend (cost estimation) from HashiCorp's charge (managed resources, Usage page, HCP portal billing) and test what the tfrun.cost_estimate fields hold. Multi-select items pair two facts from the same feature.
Terraform Professional: HCP Terraform Multi-Environment Workspaces, Version Upgrades and Policy as Code
- cloud block tags map a directory to every workspace carrying all listed tags and allow CLI workspace commands; name pins one workspace and disables them.
- TF_CLOUD_ORGANIZATION, TF_CLOUD_HOSTNAME and TF_CLOUD_PROJECT fill omitted cloud block settings; TF_WORKSPACE selects but never creates a workspace; TFE_* variables belong to the tfe provider.
- New workspaces share state with an empty specific list (pre-April-2021 ones default to organization-wide); tfe_outputs reads outputs without full state access, and its nonsensitive_values can drive for_each.
- Run triggers live on the downstream workspace, fire after a successful source apply, and auto-apply only when Auto-apply run triggers is on.
- Local execution mode keeps state storage and locking but drops workspace variables, variable sets, policies, run records and the CLI version check.
- A workspace version constraint resolves to the newest matching release; use exact versions where changes must be reviewed, and manage terraform_version per environment in tfe_workspace.
- Upgrade order: widen required_version, plan-only run on the new version, change the workspace version, then an empty apply to rewrite state; terraform-version is valid only on plan-only runs.
- sentinel test reads test/<policy>/*.hcl; a case without a test block expects a pass; run mocks need only a successful plan, expire after an hour via the API, and are not guaranteed scrubbed.
- OPA tests need input shaped as {plan, run}; the JSON plan endpoint returns the raw plan, needs workspace admin with a user or team token, and redirects to a short-lived URL.
- VCS-connected policy sets update on push (policies_path, policy_update_patterns) and refuse API uploads; unconnected sets take tar.gz versions or tfe_slug; pin runtimes with policy_tool_version (OPA rejects latest; Sentinel Agent mode has no cost data).
How the exam tests this
Expect production scenarios built around a stated requirement — least privilege, no edits when workspaces are added, changes only through review, admins first, no surprise changes — that ask which setting, command, file layout or step order meets it. Near-twin options are common: cloud block name versus tags and TF_CLOUD_* versus TFE_* variables; working directory versus branch versus trigger patterns; the three remote state sharing scopes; tfe_outputs values versus nonsensitive_values; Auto-apply versus Auto-apply run triggers; exact versions versus constraints; plan-only versus saved-plan runs; exact-version versus constraint behaviour in the CLI version check; Enabled, Beta and Disabled Terraform Enterprise versions; Sentinel test-file versus sentinel.hcl paths; input.plan versus input.run in OPA mocks; VCS-connected versus API-uploaded policy sets; and Sentinel platform versus Agent mode. Some items quote an error message or an API attribute set and ask for the cause or the valid combination, and several are select-two.
Where these Terraform Pro 002 facts came from
Terraform Pro 002 cheat sheet: your questions
Source
Exam structure, domain weights and scoring on this page come from the official Terraform Pro 002 exam guide.