Study guide · Core notes

Terraform Associate Notes: workflow and state

Reference notes for Terraform Associate (004): what the files declare, which command talks to an API, how modules and state behave, and what HCP Terraform adds.

Exam: Terraform Associate (004)8 modulesReviewed October 2026

How to read these notes: Items tagged Extra sit outside the 004 objective list, such as CLI workspaces and the older taint command. Learn the unmarked items first. New to the exam? Start with the overview for the format and the eight objectives. These notes follow Terraform 1.12 behavior described by HashiCorp.

Module 1: Infrastructure as code

Infrastructure as code means you describe infrastructure in files that a tool can create, change, and destroy. The files are the source of truth. A console click that nobody reviewed is not. Terraform's language is declarative: you describe the desired end state, and Terraform works out the actions that get there.

Why teams use it

The same files can be reviewed, stored in version control, and applied again. That makes a second environment less of a memory test. A pull request is a record of who changed the network. A console session usually is not.

Multi-cloud and hybrid

A provider is a plugin for one API. One configuration can call an AWS provider and an Azure provider, or a cloud provider and an on-premises provider. Terraform does not replace each cloud's products. It gives those APIs one workflow.

Service-agnostic workflow

The commands stay init, plan, and apply whether the resource is a virtual machine, a DNS record, or a SaaS object. What changes is the provider and the resource type, not the shape of the workflow.

Infrastructure as code does not remove drift by itself. Drift is a real object that changed outside Terraform. The next plan shows that difference only if you apply from the files. A repository nobody runs is documentation, not a control.

Module 2: Providers and why state exists

Terraform Core does not know how to call a cloud API. Providers do.

Provider pieces you set before the first apply.
PieceWhat it does
required_providersNames the source, such as hashicorp/aws, and a version constraint. terraform init downloads that plugin.
Version constraint~> 5.0 allows 5.0 and later 5.x releases, and stops before 6.0. ~> 1.2.0 allows patches in 1.2 only.
Dependency lock file.terraform.lock.hcl records the selected provider versions. Commit it for a root module so the next init can select the same builds.
provider blockSets settings such as a region. A second block needs an alias. A resource chooses it with provider = aws.west.
StateMaps each resource address to the real object Terraform manages, and stores attributes. It is not a cache you can delete and expect the next plan to be safe.

terraform init installs providers and modules and configures the backend. terraform init -upgrade selects newer versions that still match the constraints. The .terraform directory is a local cache. Do not commit it.

Module 3: The core workflow

Write configuration, initialize the working directory, review a plan, then apply. Destroy is the same workflow aimed at removal. Format is a style step you can run at any time before you commit.

Commands in the 004 workflow, and the question each one answers.
CommandWhat it answers
terraform initAre the providers, modules, and backend ready in this directory?
terraform fmtAre the files in canonical style? It rewrites whitespace and alignment. It does not validate.
terraform validateIs the configuration syntactically valid and internally consistent? It loads provider schemas and does not call the remote API. Run init first.
terraform planWhat would change? It refreshes state by default and prints creates, in-place updates, and destroys. -out saves that plan.
terraform applyMake the change. By default it shows a plan and waits for yes. Applying a saved plan file runs that plan.
terraform destroyRemove the infrastructure this state manages. It is a plan to destroy, then an apply of that plan.

In a plan, + creates, - destroys, ~ updates in place, and -/+ replaces. A replace destroys and creates because the provider cannot update that argument in place. Deleting a resource block from the configuration, then applying, destroys that one object. terraform destroy targets the whole state for this configuration.

terraform plan does not change remote infrastructure. terraform validate does not tell you whether a cloud API will accept the values. A saved plan can contain sensitive values, so treat the plan file like state.

Module 4: Configuration

Most of the exam is reading a block and saying what Terraform will do with it.

resource and data

A resource block manages an object: create, update, destroy. A data block reads an object that already exists and does not manage it. Refer to an attribute as resource_type.name.attribute. That reference is also an implicit dependency, so Terraform creates the referenced object first.

Variables, locals, and outputs

A variable block is an input. A local is a named expression inside the module. An output publishes a value to the CLI or to a calling module. Types include string, number, bool, list, set, map, object, and tuple. A list is ordered. A set is unique and unordered. A map uses string keys. An object names each attribute's type. A tuple is a fixed sequence of mixed types.

Dependencies

Use depends_on only when no reference expresses the dependency, for example a resource that must exist before another one even though you never read its attributes. create_before_destroy tells Terraform to create the replacement before it destroys the old object, so dependents are not left with a gap.

Expressions and functions

You can use conditionals, for expressions, and splat expressions such as aws_instance.web[*].id. Built-in functions transform values. You do not define a function body in a .tf file. count addresses by index, so removing an item in the middle can shift later indexes. for_each addresses by key, so the other instances stay put.

How a value is checked, hidden, or kept out of state. Objective 4g and 4h are the last three rows.
MechanismWhat a failure or a secret does
Variable validationA validation block with a condition and an error_message rejects a bad input.
Precondition and postconditionLifecycle checks that fail the operation when the condition is false. A postcondition runs after the object exists.
check blockReports a failed assertion and does not, by itself, stop the apply the way a postcondition does.
sensitive = trueRedacts CLI output. The value is still stored in state.
Ephemeral valueAvailable during the run and not written to state or the plan. Use it for a secret that must not persist.
Write-only argumentA provider argument you can set and cannot read back. Pair it with an ephemeral value so the secret is not stored.
VaultRead the secret from Vault at run time instead of committing it. The Vault provider is the usual Terraform integration.

Variable values can come from the environment as TF_VAR_name, from terraform.tfvars, from *.auto.tfvars, or from -var and -var-file. Later sources override earlier ones. Command-line flags win. Do not commit a tfvars file that holds secrets.

Module 5: Modules

A module is a directory of Terraform files you call with a module block. The caller sets the child module's input variables as arguments and reads its outputs. Root variables are not visible inside the child. Locals stay in the module that declares them. That boundary is the point of a module: a small interface instead of every variable leaking inward.

Where a module can come from. terraform init downloads it into .terraform/modules.
SourceHow you pin it
Local pathsource = "./modules/vpc". There is no version argument. You pin it by the files in the repository.
Terraform Registrysource = "namespace/name/provider". Set version to a constraint such as "~> 5.0".
GitA git URL. Select a commit, tag, or branch with a ref query on the source. The version argument is for the registry.

Changing a module source or version means running terraform init again. A module that creates resources still stores those resources in the root module's state, under an address like module.network.aws_subnet.public. The child module does not get a separate state file unless it is a separate root configuration.

Module 6: State, locking, and drift

State is the mapping Terraform trusts. Treat the file as sensitive.

Local backend

With no backend block, Terraform writes terraform.tfstate in the working directory and can keep terraform.tfstate.backup. That is fine for a solo exercise. It is a poor place for a team, because the file is on one computer and it holds attribute values, including sensitive ones.

Remote backend and locking

A backend block, or a cloud block for HCP Terraform, stores state remotely. Locking stops a second operation from writing that state at the same time. After you add or change a backend, run terraform init. Use -migrate-state to copy existing state, or -reconfigure to point at the new backend without copying.

Drift

Drift means the real object changed outside Terraform. The next plan refreshes state and proposes edits that move the object back to the configuration. terraform apply -refresh-only updates state to match reality and does not change the infrastructure. Use it when you intend to accept the outside change, not when you want the file to win.

Do not edit state by hand. terraform state list and terraform state show inspect it. A partial backend configuration leaves secrets out of the .tf file and passes them with -backend-config.

Module 7: Import, inspect, and logs

Import brings an object that already exists under Terraform's management. An import block names the resource address in to and the existing object's id. terraform plan can then show the import, and terraform plan -generate-config-out can write a starting configuration. The older terraform import command updates state only and does not write the HCL. Neither command destroys the real object. You still review the configuration so the next apply does not immediately change it.

terraform state list prints addresses. terraform state show prints one object's attributes. terraform state rm drops an address from state and leaves the real object in place, which is how you stop managing something without deleting it. terraform state mv renames an address or moves it, for example into a module. Prefer those commands, or a moved block in configuration, to hand-editing the JSON.

Set TF_LOG to TRACE, DEBUG, INFO, WARN, or ERROR when you need a diagnostic. TRACE is the most detailed. TF_LOG_PATH writes the log to a file. Logs can contain secrets and should not be pasted into a ticket. Leave TF_LOG unset for a normal apply.

Extra terraform apply -replace=address forces replacement on the next apply. It replaces the older terraform taint command. The 004 objective list names import, state inspection, and verbose logging, not taint.

Module 8: HCP Terraform

HCP Terraform is HashiCorp's hosted service. Older docs call it Terraform Cloud. Community Edition is the Terraform CLI you run yourself.

HCP Terraform ideas named by objective 8. Terraform Enterprise is the self-hosted product with a similar shape. Do not assume every hosted feature exists in Community Edition.
IdeaWhat to remember
WorkspaceThe remote unit of state, variables, and runs. One working directory connects with a cloud block that names the organization and the workspace.
ProjectA group of workspaces. Use it to organize teams and to set access above a single workspace.
Remote runPlan and apply execute in HCP Terraform, using variables and credentials stored there, instead of only on your laptop.
VCS integrationA commit or a pull request can start a run. A speculative plan from a pull request shows the diff without applying it.
GovernanceTeam access, permissions, and policy as code such as Sentinel or Open Policy Agent can block an apply that breaks a rule.
CLI workspace Extraterraform workspace select switches a named local state. It is not an HCP Terraform workspace and it is not a project.

A cloud block replaces a backend block for HCP Terraform. Initializing after you add it migrates or attaches state. Variables marked sensitive in the workspace are still available to the run. They are not a reason to commit the same secret in git.

Frequently asked questions

What is the difference between terraform plan and terraform apply?

terraform plan compares configuration and state with real infrastructure and prints the actions it would take. terraform apply carries those actions out. A plan does not change infrastructure unless you apply it.

Does sensitive = true keep a value out of state?

No. sensitive = true redacts the value in CLI output and in logs. Terraform still writes it to state. An ephemeral value is the mechanism that is not stored in state or in the plan.

What is the difference between an HCP Terraform workspace and a CLI workspace?

An HCP Terraform workspace is a remote unit of state, variables, and runs, grouped into a project. A CLI workspace is a named state file selected with terraform workspace in one working directory. Objective 8 tests the HCP Terraform workspace.

Provider arguments and HCP Terraform screen names change. When a note and the current HashiCorp documentation disagree, follow the documentation for the Terraform 1.12 behavior the 004 exam describes.

Continue the Terraform Associate study path

Use the flashcards to check whether you can recall these distinctions.

Terraform Associate hub →
Available

Overview

Exam format, Terraform 1.12, the eight objectives, and a study plan.

Available · You are here

Core Notes

Reference notes for providers, the workflow, configuration, modules, state, and HCP Terraform.

Available

Practice Exams

200 original questions with custom exams, explanations, and a score report by objective.

Related Tools

Useful companions while you study.

All study topics →