Terraform Associate 004
Tap any service to see more details and official docs.
IaC is the practice of defining and managing infrastructure through machine-readable configuration files, instead of manual configuration or UI tools. Files are versioned in VCS just like application code.
| Feature | Manual / Console | IaC with Terraform |
|---|---|---|
| Reproducibility | Depends on the operator | Identical on every run |
| Versioning | No | Git — full history |
| Review | No diff available | Pull request with terraform plan |
| Collaboration | One operator at a time | Multiple via VCS + locking |
| Rollback | Manual, error-prone | git revert + apply |
| Documentation | Implicit | The code IS the documentation |
Declarative
You describe the DESIRED state, not the steps. Terraform computes the diff and determines the necessary actions.
Provider-agnostic
One language (HCL) for AWS, Azure, GCP, Kubernetes, Datadog, GitHub and hundreds more providers.
Idempotent
Running apply multiple times produces the same result — if it already exists, it isn't recreated.
Dependency DAG
Terraform builds a dependency graph and applies changes in parallel when it is safe to do so.
| Tool | Type | When to use it |
|---|---|---|
| Terraform | Declarative · multi-cloud | Managing cloud/on-prem infrastructure. Any provider. |
| Ansible | Procedural · agentless | OS configuration, software installation, server state management. |
| CloudFormation | Declarative · AWS only | AWS infrastructure for teams that don't need multi-cloud. |
| Pulumi | Declarative · multi-cloud | Same role as Terraform but using programming languages (Python, TypeScript). |
| Chef/Puppet | Procedural · agent-based | Configuration management for server fleets with an installed agent. |
Terraform is complementary to Ansible, not a competitor. Terraform creates the infrastructure (VMs, networks, DBs). Ansible configures what's inside (installs packages, configures apps). On the exam: Terraform = provisioning, Ansible = configuration management.
Terraform's standard workflow. Each stage has a clear purpose and the order is always the same.
1. Write
Define resources in .tf files with HCL
2. Plan
Terraform shows what will change without applying
3. Apply
Applies the changes and updates the state
Click any service to see more details
| Command | Function |
|---|---|
| terraform fmt | Formats .tf files to the canonical HCL style. Equivalent to prettier. |
| terraform validate | Validates the configuration's syntax and consistency WITHOUT calling the provider API. |
| terraform apply -replace=addr | Forces recreation of a specific resource (replaces terraform taint, deprecated since v0.15.2). |
| terraform plan -refresh-only | Detects drift between state and reality without applying configuration changes (replaces terraform refresh, deprecated since v0.15.4). |
| terraform output | Shows the outputs defined in the current state. |
| terraform show | Shows the state or a saved plan in a human-readable format. |
| terraform graph | Generates the dependency graph in DOT format (viewable with Graphviz). |
| terraform force-unlock | Manually releases a state lock when an apply failed partway through. |
| terraform login | Authenticates with HCP Terraform and stores credentials locally. |
On the exam: terraform fmt and terraform validate are safe to run at any time — they don't touch real infrastructure or the state. They're pre-plan quality steps.
Terraform Core communicates with providers via RPC. Each provider is a separate binary downloaded during init. This architecture decouples Terraform from each provider's release cycle.
Click any service to see more details
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = var.aws_region
}
# Provider with alias for multiple regions
provider "aws" {
alias = "us_east"
region = "us-east-1"
}| Constraint | Meaning | Example |
|---|---|---|
| = 5.0.0 | Exactly that version | = 5.0.0 |
| != 5.0.0 | Anything except that one | != 4.0.0 |
| > 5.0 | Greater than | > 5.0 |
| ~> 5.0 | 5.x but not 6.x (free patch) | ~> 5.0 |
| ~> 5.0.0 | 5.0.x but not 5.1 (free micro) | ~> 5.0.4 |
| >= 4.0, < 6.0 | Explicit range | >= 4.0, < 6.0 |
registry.terraform.io is the official repository for providers and modules. Official providers (HashiCorp), verified (Partner) and community.
| Tier | Maintained by | Badge |
|---|---|---|
| Official | HashiCorp | hashicorp/aws, hashicorp/azurerm, hashicorp/google |
| Partner | Company with a HashiCorp agreement | Verified and supported by the partner |
| Community | Open source community | No guaranteed support |
Click any service to see more details
| Type | Example | Note |
|---|---|---|
| string | "us-east-1" | Unicode text |
| number | 42 / 3.14 | Integer or decimal |
| bool | true / false | Boolean |
| list(type) | ["a", "b", "c"] | Ordered list, indexed access |
| set(type) | toset(["a", "b", "c"]) | Unordered, no duplicates |
| map(type) | { key = "value" } | Key-value pairs of the same type |
| object({}) | { name = string, port = number } | Named typed attributes |
| tuple([]) | [string, number, bool] | List with heterogeneous types |
| any | any value | Dynamic type — Terraform infers it |
| Category | Key functions | Example |
|---|---|---|
| String | format, join, split, replace, upper, lower, trimspace | format("Hello %s", var.name) |
| Collection | length, concat, flatten, merge, keys, values, lookup | length(var.subnets) |
| Numeric | max, min, ceil, floor, abs | max(var.min_size, 2) |
| Encoding | jsonencode, jsondecode, base64encode, base64decode | jsonencode({ port = 80 }) |
| Filesystem | file, templatefile, pathexpand, abspath | file("./scripts/init.sh") |
| Date/Time | timestamp, formatdate, timeadd | formatdate("YYYY-MM-DD", timestamp()) |
| IP Network | cidrsubnet, cidrhost, cidrnetmask | cidrsubnet("10.0.0.0/8", 8, 2) |
| Meta-argument | Function |
|---|---|
| count = N | Creates N copies of the resource. Accessed with resource.name[index]. |
| for_each = map/set | Creates one instance per element. Accessed with resource.name[key]. |
| depends_on = [...] | Explicit dependency when there is no direct reference. |
| provider = alias | Uses a specific provider instance (for multiple regions/accounts). |
| lifecycle {} | Controls behavior: prevent_destroy, create_before_destroy, ignore_changes. |
| provisioner (avoid) | Runs scripts after creation. Discouraged — use cloud-init or user_data instead. |
count vs for_each: use for_each when elements have their own identity (keys). With count, removing an element from the middle renumbers all the following ones and can destroy unintended resources.
Click any service to see more details
# Call a module from the Terraform Registry
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
version = "5.0.0"
name = var.vpc_name
cidr = "10.0.0.0/16"
}
# Access the module's output
output "vpc_id" {
value = module.vpc.vpc_id
}| Source | Example | When |
|---|---|---|
| Terraform Registry | "hashicorp/consul/aws" | Public official/partner/community modules |
| Local path | "./modules/networking" | Your own modules within the same repo |
| GitHub (HTTPS) | "github.com/org/repo//modules/vpc" | Private modules on GitHub without a registry |
| GitHub (SSH) | "git@github.com:org/repo.git" | GitHub with SSH auth |
| S3 bucket | "s3::https://bucket.s3.amazonaws.com/module.zip" | Artifacts stored in S3 |
| HCP Registry | "app.terraform.io/org/module/aws" | Private modules in an HCP organization |
Always version
version = "~> 1.0" for registry modules. No version = always latest, breaks on updates.
variables.tf
Define all inputs in a separate variables.tf. Add description and validation.
outputs.tf
All outputs in outputs.tf. Expose only what's necessary — avoid exposing internals.
README.md
Document inputs, outputs and usage examples. terraform-docs generates this automatically.
Click any service to see more details
| Backend | Provider | State Locking | Note |
|---|---|---|---|
| s3 | AWS | Native (use_lockfile) | Most popular backend. Locking with use_lockfile = true (DynamoDB deprecated). |
| azurerm | Azure | Native blob lease | Azure Blob Storage. Native locking, no extra configuration. |
| gcs | Native | Google Cloud Storage. Native locking. | |
| http | Any | Optional | Generic backend over HTTP. Useful for custom backends. |
| kubernetes | K8s | Native | State in a Kubernetes ConfigMap/Secret. |
| cloud (HCP) | HCP Terraform | Native | Recommended. Locking + encryption + version history. |
| Command | Function |
|---|---|
| terraform state list | Lists all resources in the state |
| terraform state show addr | Shows detailed attributes of a resource in the state |
| terraform state mv src dst | Renames/moves a resource in the state (without touching infrastructure) |
| terraform state rm addr | Removes a resource from the state WITHOUT destroying it in the cloud |
| terraform state pull | Downloads and prints the remote state as JSON |
| terraform state push | Uploads a local state to the remote backend (dangerous — can overwrite) |
| terraform import addr id | Imports an existing resource into the state |
| terraform plan -refresh-only | Detects drift by comparing state vs reality without applying changes (terraform refresh deprecated in v0.15.4) |
terraform state rm does NOT destroy the real resource — it only removes it from Terraform's tracking. Useful for "releasing" a resource from Terraform without deleting it. After the rm, Terraform no longer manages that resource.
Click any service to see more details
| Tier | What it includes |
|---|---|
| Free | Up to 500 resources, unlimited users, remote state, remote execution, 1 concurrent run |
| Essentials / Standard / Premium | Pay per managed resource; more concurrent runs and governance features at each level |
| Terraform Enterprise | Self-hosted on your own infrastructure |
Imports existing infrastructure (created manually or by another system) into Terraform's state so it can be managed with IaC from then on.
# Before importing, define the resource in your .tf:
resource "aws_instance" "web" {
# terraform import will fill in the attributes
}
# Then import it with the resource's real ID:
terraform import aws_instance.web i-1234567890abcdef0
# Terraform 1.5+: native import block in HCL
import {
to = aws_instance.web
id = "i-1234567890abcdef0"
}After importing, run terraform plan — if the plan shows changes, your .tf configuration doesn't match the real resource. Adjust the attributes until the plan shows "No changes".
terraform taint has been deprecated since v0.15.2. Forcing a resource to be recreated is now done with the -replace flag.
| Method | Status | How it works |
|---|---|---|
| terraform taint resource.addr | ⚠️ Deprecated v0.15.2 | Marked the resource; the next apply recreates it. No visible plan beforehand. |
| terraform apply -replace=resource.addr | ✅ Modern | Shows the replacement plan first (-/+). Safer and more explicit. |
| terraform plan -replace=resource.addr | ✅ Modern | Only shows what would be recreated, without applying. |
Use -replace when a resource is in a bad state and you need to recreate it without destroying all your infrastructure. The plan shows the -/+ symbol (destroy and recreate).
| Variable | Value | Effect |
|---|---|---|
| TF_LOG | TRACE / DEBUG / INFO / WARN / ERROR | Enables detailed logging on stderr |
| TF_LOG_PATH | path/to/file.log | Saves logs to a file instead of stderr |
| TF_LOG_CORE | level | Logging only for Terraform core |
| TF_LOG_PROVIDER | level | Logging only for providers |
TF_LOG=DEBUG is the most useful level for debugging. TF_LOG=TRACE shows every call to the provider API — very verbose but useful for diagnosing authentication or throttling errors.
Required flow: init → plan → apply. No init, no plan. No plan, no correct apply.
State locking prevents concurrent applies. The local backend only locks on the same machine; teams should use a remote backend.
The state file can contain secrets in plain text. Always use a remote backend with encryption in production.
.terraform.lock.hcl must be committed to VCS. terraform.tfstate must NEVER be committed.
Variable order: -var CLI > TF_VAR_* > *.auto.tfvars > terraform.tfvars > default.
for_each > count for resources with identity. count with indices breaks when inserting/removing elements from the middle.
Terraform Registry: registry.terraform.io. Providers: hashicorp/aws. Modules: org/name/provider.
HCP Terraform ≠ CLI workspaces. They are distinct concepts. An HCP workspace = an isolated environment with state, vars and VCS.
sensitive = true hides it in the CLI but the value IS in the state. ephemeral = true (1.10+) is the only way to ensure a value is NEVER persisted in the state.
terraform taint deprecated (v0.15.2) → use terraform apply -replace. terraform refresh deprecated (v0.15.4) → use terraform plan -refresh-only.
This guide is a quick summary. Visit the Deep Dive section for an exhaustive walkthrough with real cases, cost analysis and architecture decisions.
Put what you learned to the test with original Terraform Associate exam questions.
Start Terraform Associate mock examUnlimited mock exams with the Pro plan
Was this guide helpful?
Save your email so you don't have to type it again — pick up later and unlock Terraform Associate with a Pro plan or individual purchase.