Exam Identity
| Item | Reference |
|---|
| Vendor/provider | HashiCorp |
| Official exam title | HashiCorp Certified: Terraform Associate (004) |
| Official exam code | 004 |
| Page purpose | Independent quick reference for real-exam preparation and original practice support |
Use this page to quickly review the Terraform CLI workflow, configuration language, state behavior, providers, modules, and managed-workflow concepts that commonly drive exam questions.
| Concept | What it does | Exam distinction |
|---|
| Terraform | Infrastructure as code tool for defining, planning, and applying infrastructure changes | Declarative: describe desired end state, not step-by-step procedures |
| Configuration | .tf / .tf.json files written in HCL or JSON | Terraform loads all files in the working directory as one configuration |
| Provider | Plugin that lets Terraform manage a target platform or service | Provider exposes resource types and data sources |
| Resource | Managed infrastructure object | Terraform creates, updates, replaces, or destroys it |
| Data source | Reads external or existing information | Does not create infrastructure |
| State | Mapping between configuration and real remote objects | Required for tracking, planning, dependencies, and drift detection |
| Plan | Proposed execution graph and change set | Shows intended changes before apply |
| Apply | Executes a plan | Can apply an already saved plan or create a new plan interactively |
| Module | Container for Terraform configuration | Every configuration has a root module; child modules are reusable |
| Backend | Where state is stored and how operations may be coordinated | Some backends support remote state locking and remote operations |
| Workspace | Separate state instance for the same configuration | Useful for environment separation in limited cases; not a substitute for all environment design |
flowchart LR
A[Write configuration] --> B[terraform fmt]
B --> C[terraform init]
C --> D[terraform validate]
D --> E[terraform plan]
E --> F[terraform apply]
F --> G[terraform show / output]
G --> H[Change config]
H --> D
| Step | Command | Purpose | Common exam trap |
|---|
| Format | terraform fmt | Rewrites configuration to canonical style | Formatting does not validate provider credentials or remote APIs |
| Initialize | terraform init | Downloads providers/modules and configures backend | Required before plan/apply in a new or changed working directory |
| Validate | terraform validate | Checks syntax and internal consistency | Does not contact all remote APIs to prove resources can be created |
| Plan | terraform plan | Builds proposed changes | A plan is not a guarantee that apply will succeed |
| Apply | terraform apply | Creates/updates/destroys infrastructure | Without saved plan, Terraform creates a fresh plan before applying |
| Destroy | terraform destroy | Removes managed infrastructure | Equivalent intent to planning/applying destroy actions |
| Inspect | terraform show, terraform state, terraform output | Reads state, plans, or outputs | output is for declared output values, not arbitrary state browsing |
High-Yield CLI Command Matrix
| Command | Use when | Key options / notes |
|---|
terraform version | Confirm CLI version and provider selections | Helpful for troubleshooting lock-file or version mismatch issues |
terraform init | Start using a configuration, backend, provider, or module | Re-run after changing backend, modules, or required providers |
terraform init -upgrade | Upgrade provider/module selections within version constraints | Updates selections; do not confuse with normal init |
terraform fmt | Normalize HCL formatting | Use -recursive to format subdirectories |
terraform validate | Check configuration validity | Requires initialized providers for complete validation |
terraform plan | Preview changes | Use -out=FILE to save an exact plan |
terraform apply | Execute changes | terraform apply FILE applies a saved plan |
terraform destroy | Destroy managed objects | Often used for teardown in temporary environments |
terraform output | Read root module outputs | -json is useful for automation |
terraform show | Display state or plan contents | -json produces machine-readable output |
terraform state list | List tracked resource addresses | State inspection only; does not query all cloud inventory |
terraform state show ADDRESS | Inspect one tracked object | Useful for ID and attribute review |
terraform state mv | Rename/move state binding | Used when refactoring addresses without replacing infrastructure |
terraform state rm | Stop tracking an object | Does not destroy the remote object |
terraform import | Bind existing infrastructure to a resource address | Requires matching configuration for normal ongoing management |
terraform taint / untaint | Mark resource for replacement / undo | Superseded in many workflows by -replace, but still exam-relevant |
terraform apply -replace=ADDRESS | Force replacement during apply | Safer, explicit replacement targeting |
terraform workspace list | Show workspaces | Current workspace is marked |
terraform workspace new NAME | Create workspace | Creates separate state for same configuration |
terraform workspace select NAME | Switch workspace | Be careful before plan/apply |
terraform providers | Show provider requirements | Useful for module/provider troubleshooting |
terraform graph | Output dependency graph | Produces DOT format for visualization |
terraform console | Evaluate expressions interactively | Useful for functions, variables, and type behavior |
terraform login | Store credentials for HCP Terraform/Terraform Enterprise | Writes credentials for remote service access |
terraform logout | Remove stored credentials | Cleans CLI credentials |
Configuration File Roles
| File pattern | Common purpose | Required? |
|---|
main.tf | Main resources, modules, data sources | No; naming is conventional |
variables.tf | Input variable declarations | No |
outputs.tf | Output declarations | No |
providers.tf | Provider and Terraform settings | No |
versions.tf | Required Terraform and provider constraints | No, but common |
terraform.tfvars | Variable values | No; auto-loaded if present |
*.auto.tfvars | Variable values auto-loaded by lexical order | No |
.terraform.lock.hcl | Dependency lock file for providers | Created/updated by init; should usually be committed for reproducibility |
.terraform/ | Local working directory data | Do not treat as source configuration |
terraform.tfstate | Local state file if using local backend | Protect carefully; often replaced by remote backend in teams |
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "example-tf-state"
key = "network/terraform.tfstate"
region = "us-east-1"
}
}
provider "aws" {
region = var.region
}
| Block | Purpose | Exam notes |
|---|
terraform | Configures Terraform behavior | Includes required version, required providers, and backend |
required_version | Constrains Terraform CLI version | Protects configuration from unsupported CLI versions |
required_providers | Declares provider source and version constraint | Required for clear provider dependency management |
provider | Configures provider instance | Credentials are commonly supplied outside code via environment, profile, or remote workspace variables |
backend | Configures state storage | Backend blocks cannot use normal input variables |
Version Constraint Quick Reference
| Constraint | Meaning |
|---|
= 1.5.7 | Exactly this version |
!= 1.5.7 | Exclude this version |
> 1.5.0, >= 1.5.0 | Greater than / greater than or equal |
< 2.0.0, <= 2.0.0 | Less than / less than or equal |
~> 1.5.0 | Compatible patch releases: >= 1.5.0 and < 1.6.0 |
~> 1.5 | Compatible minor releases: >= 1.5.0 and < 2.0.0 |
Common trap: provider version constraints define allowed versions; .terraform.lock.hcl records selected versions. terraform init -upgrade can choose newer versions allowed by constraints.
Resource, Data Source, and Output Syntax
resource "aws_instance" "web" {
ami = data.aws_ami.al2023.id
instance_type = var.instance_type
tags = {
Name = "${var.environment}-web"
}
}
data "aws_ami" "al2023" {
most_recent = true
owners = ["amazon"]
}
output "web_instance_id" {
value = aws_instance.web.id
description = "ID of the web instance"
}
| Construct | Address form | Purpose |
|---|
| Resource | aws_instance.web | Managed object |
| Data source | data.aws_ami.al2023 | Read-only lookup |
| Output | output.web_instance_id conceptually; accessed by name | Exposes values from root or child module |
| Module | module.network | Child module call |
| Count instance | aws_instance.web[0] | Numeric index |
| For-each instance | aws_instance.web["blue"] | Keyed instance |
variable "environment" {
type = string
description = "Deployment environment"
nullable = false
validation {
condition = contains(["dev", "stage", "prod"], var.environment)
error_message = "environment must be dev, stage, or prod."
}
}
variable "tags" {
type = map(string)
default = {}
}
locals {
common_tags = merge(var.tags, {
Environment = var.environment
ManagedBy = "Terraform"
})
}
output "common_tags" {
value = local.common_tags
sensitive = false
}
| Item | Use for | Key distinction |
|---|
| Input variable | External parameter to a module | Values are assigned from CLI, files, environment, defaults, or calling module |
| Local value | Internal named expression | Cannot be overridden from outside |
| Output value | Return value from module | Root outputs display after apply; child outputs are accessed as module.name.output |
sensitive = true | Redact value in CLI output | Sensitive values can still exist in state; protect state |
validation | Enforce input rules | Runs during plan/apply validation |
nullable = false | Prevent null assignment | Useful for required explicit values |
Variable Value Precedence
Highest priority wins.
| Priority | Source |
|---|
| High | -var and -var-file command-line options |
| *.auto.tfvars / *.auto.tfvars.json files |
| terraform.tfvars / terraform.tfvars.json |
| Environment variables named TF_VAR_name |
| Low | Variable default value |
If no value is available and no default exists, Terraform prompts interactively unless input is disabled.
Type System Reference
| Type | Example | Notes |
|---|
string | "prod" | Text |
number | 3 | Numeric values |
bool | true | Boolean |
list(string) | ["a", "b"] | Ordered, same element type |
set(string) | toset(["a", "b"]) | Unordered, unique values |
map(string) | { env = "prod" } | String keys, same value type |
object({...}) | object({ name = string, size = number }) | Named attributes with defined types |
tuple([...]) | tuple([string, number]) | Ordered elements with possibly different types |
any | any | Avoid unless truly flexible |
null | null | Omits or unsets an argument in many contexts |
Expressions and Functions
| Pattern | Example | Use |
|---|
| Reference | var.region, aws_vpc.main.id | Access object attributes |
| String template | "web-${var.environment}" | Interpolate values |
| Conditional | var.create ? 1 : 0 | Choose between two expressions |
| For expression list | [for s in var.subnets : s.id] | Transform list |
| For expression map | { for s in var.subnets : s.name => s.id } | Build map |
| Splat | aws_instance.web[*].id | Collect attribute from multiple instances |
| Dynamic block | dynamic "ingress" { ... } | Generate nested blocks |
| Type conversion | tolist(var.ids), toset(var.ids) | Normalize types |
Common Functions
| Function | Typical use |
|---|
length() | Count elements or characters |
lookup() | Read map value with default |
merge() | Combine maps; later keys win |
concat() | Combine lists |
contains() | Test if list/set contains value |
coalesce() | First non-null, non-empty string argument |
try() | Fallback when expression errors |
can() | Test whether expression can be evaluated |
file() | Read local file content |
templatefile() | Render template file with variables |
jsonencode() | Produce JSON from Terraform values |
yamldecode() | Parse YAML into Terraform values |
flatten() | Flatten nested lists |
setproduct() | Cartesian product of sets/lists |
cidrsubnet() | Derive subnet CIDR blocks |
timestamp() | Current timestamp; can cause changing plans if used carelessly |
| Meta-argument | Applies to | Purpose | Trap |
|---|
count | Resources, modules, some blocks | Create N instances by numeric index | Index changes can cause unintended replacement |
for_each | Resources, modules, dynamic blocks | Create instances by stable keys | Usually safer than count for named objects |
depends_on | Resources, modules | Explicit dependency | Use only when implicit references are insufficient |
provider | Resources, modules | Select provider configuration or alias | Needed for multiple provider instances |
lifecycle | Resources | Customize create/update/destroy behavior | Does not override provider/API reality |
provider "aws" {
alias = "west"
region = "us-west-2"
}
resource "aws_s3_bucket" "logs" {
provider = aws.west
bucket = var.bucket_name
lifecycle {
prevent_destroy = true
}
}
Lifecycle Rules
| Lifecycle argument | Effect | Exam note |
|---|
create_before_destroy | Attempts replacement by creating new object first | Requires remote platform to allow overlapping objects |
prevent_destroy | Blocks plans that would destroy object | Must be removed or changed before destruction can proceed |
ignore_changes | Ignores selected attribute drift | Useful for externally managed fields; can hide real drift |
replace_triggered_by | Replaces when referenced object/expression changes | Explicit replacement dependency |
Dependency Graph and Planning
| Dependency source | Example | Notes |
|---|
| Implicit dependency | Resource references another resource attribute | Preferred; Terraform infers order |
| Explicit dependency | depends_on = [aws_iam_role.app] | Use for hidden dependencies not visible through expressions |
| Provider dependency | Resource requires provider configuration | Initialized through provider plugin |
| Destroy dependency | Terraform calculates safe destroy order | Usually reverse of creation dependencies |
| Unknown values | (known after apply) in plan | Values are not known until provider creates or reads object |
Plan symbols often seen in CLI output:
| Symbol | Meaning |
|---|
+ | Create |
- | Destroy |
~ | Update in place |
-/+ or +/- | Replace |
<= | Read data source |
-/+ with reason | Attribute or lifecycle rule requires replacement |
State Essentials
| State concept | What to remember |
|---|
| State maps addresses to remote object IDs | Without state, Terraform cannot know which real objects it manages |
| State stores attributes | May contain sensitive values; secure it |
| State supports dependency metadata | Helps order operations |
| State enables drift detection | Refresh compares state with remote objects |
| State should not be manually edited | Use Terraform commands whenever possible |
| Remote state improves collaboration | Central storage, access control, and locking depend on backend/service capabilities |
State Commands
| Task | Command | Effect |
|---|
| List tracked objects | terraform state list | Shows addresses |
| Inspect object | terraform state show ADDRESS | Shows attributes stored in state |
| Move address | terraform state mv OLD NEW | Changes binding without remote change |
| Remove binding | terraform state rm ADDRESS | Forgets object; does not destroy it |
| Pull state | terraform state pull | Writes current state to stdout |
| Push state | terraform state push | Replaces remote state; use cautiously |
State Refactoring Options
| Scenario | Best tool | Why |
|---|
| Rename resource block but keep same object | moved block or terraform state mv | Avoids destroy/recreate due to address change |
| Split configuration into modules | moved block or state move | Preserves bindings |
| Stop managing object but leave it running | terraform state rm | Removes from state only |
| Bring existing object under Terraform | terraform import plus configuration | Creates state binding |
| Force replacement of one object | terraform apply -replace=ADDRESS | Explicit replacement without broad targeting |
moved {
from = aws_instance.web
to = aws_instance.app
}
Import Reference
Terraform import associates an existing remote object with a Terraform resource address. It does not automatically guarantee complete, correct configuration for future management.
resource "aws_s3_bucket" "existing" {
bucket = "example-existing-bucket"
}
terraform import aws_s3_bucket.existing example-existing-bucket
terraform plan
| Import step | Purpose |
|---|
| Write or generate resource configuration | Terraform needs a resource address to bind |
Run terraform import ADDRESS ID | Add binding to state |
Run terraform plan | Detect missing arguments, drift, or changes Terraform would make |
| Adjust configuration | Align desired configuration with actual object |
Backends and State Storage
| Backend concept | Meaning | Exam distinction |
|---|
| Local backend | Stores state on local disk | Simple but poor for team collaboration |
| Remote backend | Stores state in remote service/storage | Better for sharing, locking, and security depending on backend |
| Backend configuration | Defined in terraform block or partial config | Backend blocks cannot use normal variables |
| State locking | Prevents concurrent writes | Support depends on backend |
| Backend migration | Moving state from one backend to another | terraform init prompts or options guide migration |
terraform {
backend "remote" {
organization = "example-org"
workspaces {
name = "network-prod"
}
}
}
Common trap: backend is not a provider. Providers manage infrastructure APIs; backends store Terraform state and coordinate operations.
Workspaces
| Workspace type | Meaning |
|---|
| CLI workspace | Named state instance for a single configuration and backend |
| HCP Terraform workspace | Managed unit for runs, variables, state, VCS integration, and settings |
| Default workspace | Created automatically for CLI workspaces |
| Use workspaces when | Avoid relying only on workspaces when |
|---|
| You need multiple state instances for similar configuration | Environments require significantly different access, topology, or lifecycle |
You want quick separation such as dev and test for the same root module | Strong isolation, separate credentials, or separate repositories are required |
| Backend supports clean workspace state separation | Naming mistakes could apply changes to the wrong environment |
terraform workspace list
terraform workspace new dev
terraform workspace select dev
terraform plan
Modules
| Module concept | Reference |
|---|
| Root module | Current working directory |
| Child module | Called by a module block |
| Published module | Retrieved from registry, VCS, local path, or archive source |
| Module input | Variable declared in child and assigned in module block |
| Module output | Output declared in child and accessed as module.name.output |
| Module version | Pin or constrain modules from registries for repeatability |
module "network" {
source = "app.terraform.io/example-org/vpc/aws"
version = "~> 1.2"
cidr_block = var.vpc_cidr
environment = var.environment
}
output "vpc_id" {
value = module.network.vpc_id
}
Module Source Decision Table
| Source type | Example | Notes |
|---|
| Local path | ./modules/vpc | Good for same repository |
| Public registry | hashicorp/consul/aws | Supports version argument |
| Private registry | app.terraform.io/org/name/provider | Common in HCP Terraform/Terraform Enterprise |
| Git | git::https://example.com/repo.git//modules/vpc?ref=v1.0.0 | Pin with ref for reproducibility |
| HTTP archive | URL to archive | Less common; ensure integrity and repeatability |
Module Traps
| Trap | Correction |
|---|
| Changing module source but not reinitializing | Run terraform init |
| Referencing child resource directly from root | Expose needed value through child output |
| Using unpinned remote module source | Pin versions or refs for stable runs |
| Confusing provider inheritance | Child modules can inherit default providers, but aliases often need explicit passing |
| Assuming module equals separate state | Modules share the root module state unless split into separate configurations/backends |
Provider Configuration and Aliases
provider "aws" {
region = "us-east-1"
}
provider "aws" {
alias = "dr"
region = "us-west-2"
}
module "replica" {
source = "./modules/replica"
providers = {
aws = aws.dr
}
}
| Need | Use |
|---|
| One default provider configuration | Single provider block |
| Multiple regions/accounts/endpoints | Provider aliases |
| Pass aliased provider to child module | providers map in module block |
| Control provider versions | required_providers in terraform block |
| Authenticate securely | Environment variables, profiles, workload identity, or remote workspace secrets rather than hardcoding |
Provisioners
| Provisioner | Use | Exam guidance |
|---|
local-exec | Runs command on machine running Terraform | Not idempotent by default; use sparingly |
remote-exec | Runs command on created remote resource | Requires connection details; fragile |
file | Copies files to remote resource | Requires connection details |
| Creation-time provisioner | Runs after create | Failure may taint resource |
| Destroy-time provisioner | Runs before destroy | Must still be reachable and configured |
Prefer provider-native resources, image baking, cloud-init/user data, configuration management, or platform automation over provisioners when possible. Provisioners are a last resort.
| Concept | What it provides | Exam distinction |
|---|
| HCP Terraform | HashiCorp-managed Terraform workflow platform | Remote state, runs, variables, VCS workflows, registry, teams, policy features depending on edition/configuration |
| Terraform Enterprise | Self-hosted distribution for organizations | Similar workflow concepts, operated by the customer |
| Workspace | Unit of state, variables, runs, and settings | Not identical to CLI workspace behavior |
| Remote run | Plan/apply executed remotely | Keeps credentials and execution environment centralized |
| VCS-driven workflow | Runs triggered from version control changes | Common team workflow |
| CLI-driven workflow | Local CLI starts remote operations | Useful when retaining CLI workflow with remote execution/state |
| API-driven workflow | Automation triggers runs through API | CI/CD integration pattern |
| Variable sets | Reusable variables across workspaces | Helps standardize environment/global values |
| Private registry | Internal module/provider sharing | Promotes reuse and governance |
| Policy as code | Rules checked against plans | Sentinel is HashiCorp’s policy as code framework |
| Run tasks | Integrations during run workflow | Can connect external checks or tools |
| Workflow | Choose when | Watch for |
|---|
| Local CLI with local state | Solo experiments, disposable labs | Poor collaboration and state protection |
| Local CLI with remote backend | CLI workflow plus shared state | Credentials may still be local unless using remote execution |
| HCP Terraform remote execution | Team workflow with centralized runs and variables | Understand workspace configuration and variable handling |
| VCS-driven remote runs | Pull request / merge-based infrastructure changes | Plan and apply behavior depends on workspace settings |
| API-driven runs | External automation orchestrates Terraform | Requires token and run lifecycle handling |
Security and Sensitive Data
| Area | Best practice | Exam trap |
|---|
| Provider credentials | Use environment variables, profiles, dynamic credentials, or workspace variables | Do not hardcode secrets in .tf files |
| State | Store remotely with access control and locking where possible | sensitive = true does not remove values from state |
| Variable files | Keep secret .tfvars out of public repositories | Auto-loaded files can accidentally leak values |
| Outputs | Mark sensitive outputs with sensitive = true | Redacts display, not necessarily storage |
| Modules | Use trusted sources and pin versions | Unpinned modules can change unexpectedly |
| Plans | Treat saved plan files as sensitive | Plans may contain secret values |
| Lock file | Commit .terraform.lock.hcl for provider reproducibility | Deleting it can change provider selections on init |
Drift, Refresh, and Reconciliation
| Situation | Terraform behavior |
|---|
| Remote object changed outside Terraform | Plan may show drift and propose correction |
| Remote object deleted outside Terraform | Plan may propose recreation |
| Configuration changed | Plan compares desired config with refreshed state |
| State changed without remote change | Plan may propose unexpected actions |
ignore_changes used | Selected drift may be ignored |
| Data source value changed | Dependent resources may update or replace depending on attributes |
Useful review command pattern:
terraform init
terraform validate
terraform plan
terraform show
Targeting and Replacement
| Option | Use | Caution |
|---|
-target=ADDRESS | Focus operation on specific resource/module and dependencies | For exceptional recovery, not routine workflow; can create partial convergence |
-replace=ADDRESS | Force replacement of one resource instance | Clearer than taint-based workflow |
terraform taint ADDRESS | Mark object for replacement in next plan | Leaves state marked until replacement or untaint |
terraform untaint ADDRESS | Remove taint mark | Only if replacement is no longer wanted |
Common exam point: Terraform normally plans across the full dependency graph. Targeting is an exception.
Dynamic Blocks and Iteration
resource "aws_security_group" "web" {
name = "web"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
}
| Feature | Best for |
|---|
count | Simple number of nearly identical instances |
for_each | Stable instances keyed by names/IDs |
dynamic block | Repeating nested blocks inside a resource |
for expression | Transforming collections |
| Splat expression | Extracting attributes from repeated resources |
Count vs For_each
| Question | Prefer count | Prefer for_each |
|---|
| Are instances identical and only quantity matters? | Yes | No |
| Do instances have stable names/keys? | No | Yes |
| Could removing one middle element shift indexes? | Risky | Safer |
| Address form | resource.type.name[0] | resource.type.name["key"] |
| Input shape | Number or list length | Map or set of strings |
Null, Unknown, and Sensitive Values
| Value type | Meaning | Plan behavior |
|---|
null | Absence/omission | May let provider default apply |
| Unknown | Value known only after apply | Displayed as known after apply |
| Sensitive | Redacted in CLI | Still participates in expressions and may be stored |
| Computed | Provider returns value | Often unknown until create/read |
Files, Directories, and Loading Behavior
| Behavior | Reference |
|---|
Terraform loads .tf and .tf.json files in the current directory | File names are for humans except special variable files |
| Subdirectories are not automatically included | Use modules to include subdirectories |
| Root module is the working directory | Running Terraform from a different directory changes the root module |
.terraform/ stores local init data | Regenerated by terraform init |
| Provider plugins are installed during init | Selected versions recorded in lock file |
Useful Environment Variables
| Variable | Purpose |
|---|
TF_VAR_name | Supplies value for input variable name |
TF_LOG | Enables Terraform logging for troubleshooting |
TF_LOG_PATH | Writes logs to file |
TF_CLI_ARGS | Adds default CLI arguments globally |
TF_CLI_ARGS_plan | Adds default arguments to specific command |
TF_INPUT | Controls interactive prompting |
TF_WORKSPACE | Selects workspace for automation scenarios |
Use CLI argument environment variables carefully; hidden defaults can confuse troubleshooting.
Troubleshooting Decision Table
| Symptom | Likely area | First checks |
|---|
terraform init fails downloading provider | Provider source/version/network/auth | required_providers, lock file, registry access, version constraints |
| Backend init fails | Backend configuration/auth | Backend block, partial config, credentials, state access |
| Plan wants to recreate renamed resource | Address changed | Add moved block or use terraform state mv |
| Plan shows changes you did not code | Drift or provider default changes | Review refresh results, remote object, provider version |
| Data source fails | Query too broad/narrow or credentials | Filter arguments, provider permissions, region/account |
| Module output unavailable | Output not declared in child module | Add child output, reference module.name.output |
| Aliased provider not used | Provider inheritance/configuration | Add provider = on resource or providers map on module |
| Workspace applied wrong environment | Workspace selection | terraform workspace show, backend/workspace settings |
| Sensitive value printed or stored | Output/state handling | Mark outputs sensitive; secure state and plan files |
| Lock error | Concurrent operation or stale lock | Confirm no active run; use backend-supported unlock process carefully |
Common Exam Distinctions
| Do not confuse | Correct distinction |
|---|
| Provider vs backend | Provider manages infrastructure; backend stores state |
| Resource vs data source | Resource manages; data source reads |
| Variable vs local | Variable is external input; local is internal expression |
| Output vs state | Output exposes selected values; state stores managed object data |
| Module vs workspace | Module organizes/reuses config; workspace separates state |
fmt vs validate | fmt formats; validate checks configuration |
plan vs apply | Plan proposes; apply executes |
state rm vs destroy | state rm forgets; destroy deletes remote object |
import vs create | Import tracks existing object; create provisions new object |
count vs for_each | Count indexes numerically; for_each keys instances |
| Sensitive output vs secret storage | Sensitive redacts display, but state still requires protection |
| Saved plan vs normal apply | Saved plan applies exact reviewed actions; normal apply creates a new plan |
Compact Pre-Exam Checklist
- Know the default workflow:
fmt, init, validate, plan, apply. - Know what state is, why it matters, and why it must be protected.
- Be able to choose between resources, data sources, variables, locals, outputs, modules, and providers.
- Understand implicit dependencies through references and when
depends_on is appropriate. - Review
count, for_each, dynamic blocks, and address formats. - Understand backends, locking, remote state, and workspace separation.
- Know when to use
state mv, state rm, import, moved, -replace, and -target. - Review module source formats, version pinning, inputs, outputs, and provider passing.
- Understand HCP Terraform/Terraform Enterprise workflow concepts: workspaces, remote runs, VCS, variables, private registry, and policy checks.
- Remember that sensitive values can still exist in state and plan files.
Practical Next Step
After reviewing this Quick Reference, practice with scenario-based questions that ask which Terraform command, block, state operation, module pattern, or HCP Terraform workflow best fits a described situation.