Domain 1: Manage Resource Lifecycle
- terraform init downloads providers and modules, configures the backend, and writes the dependency lock file; -upgrade re-selects newer provider/module versions within constraints, -reconfigure ignores existing backend state while -migrate-state moves existing state to a new backend.
- terraform plan -detailed-exitcode returns 0 for no changes, 2 for changes present, and 1 for an error, which is what automation uses to decide whether an apply is needed.
- Saving a plan with terraform plan -out=tfplan and then running terraform apply tfplan applies exactly that plan without re-prompting, guaranteeing the applied changes match what was reviewed.
- terraform apply -replace=ADDRESS forces one resource to be destroyed and recreated and is the supported replacement for the deprecated terraform taint command.
- -target lets you limit a plan or apply to specific resources, but HashiCorp treats it as a troubleshooting or recovery tool rather than a routine workflow.
- terraform destroy is equivalent to terraform apply -destroy, and a resource with lifecycle prevent_destroy = true will cause the plan to error rather than be destroyed.
- terraform state subcommands operate on state directly: mv renames or moves an address, rm removes a resource from state without destroying the real infrastructure, and list/show inspect it.
- The config-driven import {} block (with to and id) plans imports as part of the normal workflow and can generate starter configuration with -generate-config-out, unlike the older one-off terraform import CLI command.
- terraform plan -refresh-only and apply -refresh-only reconcile state with real-world drift by updating the state file to match reality, without proposing configuration-driven changes.
- A moved {} block refactors a resource to a new address (for example when renaming or moving it into a module) so Terraform updates state in place instead of destroying and recreating the resource.
- State locking prevents concurrent writes; if a run crashes and leaves a stale lock, terraform force-unlock with the lock ID releases it, and it should be used with care.
Domain 2: Develop and Troubleshoot Dynamic Configuration
- Variable validation {} blocks enforce input rules with a condition and error_message, while precondition and postcondition blocks (in lifecycle) and check {} blocks assert assumptions during plan and apply.
- try() returns the first argument that succeeds and can() returns a bool for whether an expression evaluates without error, giving you graceful handling of values that may not exist.
- for expressions build lists ([for x in coll : ...]) or maps ({for k, v in coll : k => v}) and support an if clause to filter, which is the idiomatic way to transform collections.
- for_each on a resource takes a map or a set of strings and keys instances by that value (each.key/each.value), whereas count keys instances by a numeric index (count.index).
- Converting a resource from count to for_each (or reordering a count list) changes instance addresses and, without moved blocks, causes Terraform to destroy and recreate resources.
- dynamic blocks generate repeatable nested blocks (such as multiple ingress rules) from a collection, keeping configuration DRY.
- Variable definition precedence, from highest to lowest, is: -var and -var-file on the command line, then *.auto.tfvars (alphabetical) and terraform.tfvars, then TF_VAR_ environment variables, then the variable default.
- Type constraints can be primitive (string/number/bool) or complex (list, set, map, object, tuple), and object attributes can be marked optional(type, default) to supply defaults for omitted keys.
- Marking a variable or output sensitive = true keeps its value out of plan and apply output; sensitivity propagates through expressions, and nonsensitive() must be used deliberately to remove it.
- Sensitive values are still stored in plain text in the state file, so best practice is to source secrets dynamically (for example from Vault or a cloud secrets manager) rather than committing them.
- count and for_each cannot depend on values that are only known after apply; when they do, Terraform errors and you must restructure the configuration or use -target to create the dependency first.
Domain 3: Develop Collaborative Terraform Workflows
- required_version pins the Terraform core version and required_providers pins provider versions; the ~> pessimistic operator (for example ~> 5.2 allows 5.x but not 6.0) is the common way to allow patches while blocking breaking changes.
- The .terraform.lock.hcl dependency lock file records the exact provider versions and hashes selected; commit it so every team member and CI run uses identical providers, and update it deliberately with terraform init -upgrade.
- Remote backends (such as S3 with DynamoDB locking, azurerm, gcs, or HCP) store state centrally and provide state locking so team members do not corrupt state with concurrent applies.
- Partial backend configuration lets you keep secrets out of the committed backend block and supply them at init with -backend-config, either as a file or key=value pairs.
- The terraform_remote_state data source reads another configuration's root-level outputs, which is the standard way to share values (like a VPC ID) from a network layer to an application layer.
- For automation, run non-interactively with -input=false and a saved plan (plan -out then apply the plan file), set TF_IN_AUTOMATION, and use -detailed-exitcode to gate the apply stage.
- CLI workspaces (terraform.workspace, managed with terraform workspace new/select) maintain multiple named states for one configuration and are a different concept from HCP Terraform workspaces.
- Never commit state files or the .terraform directory; state can contain secrets, and .terraform holds machine-specific provider binaries.
- Layering many small states (network, platform, app) with explicit output-to-remote-state dependencies scales better and limits blast radius compared with one monolithic state.
- Version constraints apply to the Terraform binary, providers, and modules independently, and a team should pin all three for reproducible runs.
- Credentials in automation should come from short-lived, dynamic sources (OIDC / dynamic provider credentials) or a secrets manager, never from static values committed to the repository.
Domain 4: Create, Maintain, and Use Terraform Modules
- A module is any directory of .tf files; the root module is what Terraform runs, and it calls child modules with module blocks that pass input variables and read the module's outputs.
- The source argument supports local paths (./modules/x), Terraform Registry references (namespace/name/provider), and git/github/http/S3 sources; the version argument is only valid for registry and HCP sources, not for git or local paths.
- For git sources you pin a revision with a ref argument (ref=v1.2.0 or a commit SHA) instead of the version argument.
- Reusable modules should generally not contain their own provider blocks; the calling configuration passes providers in, either implicitly or explicitly with providers = { aws = aws.west } for aliased providers.
- A module can be instantiated multiple times with for_each or count, and depends_on can order an entire module relative to other resources.
- Refactoring existing resources into a new child module without destroying them is done with moved blocks that map old addresses to the new module.resource addresses.
- The ~> version constraint and semantic versioning let you consume registry modules safely, upgrading minor and patch releases while guarding against breaking major versions.
- A well-designed module exposes a small, intention-revealing set of input variables and outputs and keeps the root configuration thin, composing behavior from child modules.
- Module outputs are the only way a calling configuration can read values from inside a module, so anything consumers need must be surfaced as an output.
- Publishing to the public or a private module registry requires the standard module structure and semantic version tags, enabling discovery and version constraints.
- Avoid excessively deep module nesting; prefer shallow composition, because deep nesting makes inputs, outputs, and provider passing hard to reason about.
Domain 5: Configure and Use Terraform Providers
- Terraform has a plugin-based architecture: providers are plugins downloaded during init from a registry, addressed as hostname/namespace/type (for example registry.terraform.io/hashicorp/aws), in official, partner, and community tiers.
- required_providers inside the terraform {} block declares each provider's source and version constraint, and it is required for any provider that is not a legacy HashiCorp default.
- Provider aliasing (alias = "west") creates additional named configurations of the same provider, and resources or modules select one with provider = aws.west.
- The .terraform.lock.hcl file locks provider versions and records hashes per platform; terraform providers lock -platform=... adds hashes for other operating systems so CI on a different OS does not fail on a hash mismatch.
- terraform init -upgrade is what updates providers to newer allowed versions and refreshes the lock file; without it, init respects the versions already locked.
- Provider authentication should use environment variables, assume_role, or dynamic credentials/OIDC rather than static keys in configuration; each aliased provider can carry its own credentials.
- A provider block configures a provider's settings (region, endpoints, credentials); the default (unaliased) configuration is used unless a resource explicitly selects an alias.
- Common provider errors include a missing required_providers entry, a lock-file hash mismatch, a version constraint conflict, and 'provider produced inconsistent final plan' bugs; TF_LOG enables debug logging.
- Provider mirrors (filesystem or network) let air-gapped or controlled environments install providers without reaching the public registry.
- terraform state replace-provider rewrites the provider source in state, which is needed when a provider's namespace changes (for example a fork or a moved registry address).
- Passing aliased providers explicitly into modules keeps multi-region or multi-account configurations correct, since a module does not inherit non-default provider configurations automatically.
Domain 6: Collaborate on Infrastructure as Code Using HCP Terraform
- HCP Terraform (formerly Terraform Cloud) supports three run workflows: VCS-driven (triggered by commits/PRs), CLI-driven (connected with a cloud {} block), and API-driven; the CLI connects using the cloud block or the remote backend.
- Workspaces have an execution mode of remote (runs execute in HCP) or local (HCP stores state but runs execute on your machine), plus settings for the Terraform version, VCS connection, working directory, and auto-apply.
- Speculative plans run automatically on pull requests to show the effect of a change without applying it, supporting review before merge.
- Workspace variables are categorized as terraform (input variables) or env (environment variables), and either can be marked sensitive; variable sets share variables across many workspaces or projects.
- Dynamic provider credentials / OIDC let HCP Terraform obtain short-lived cloud credentials for each run, removing the need to store long-lived static secrets in workspace variables.
- Policy as code uses Sentinel or OPA policy sets with enforcement levels of advisory (warn), soft-mandatory (can be overridden by authorized users), and hard-mandatory (blocks the run).
- Run tasks integrate external systems at defined stages (for example pre-plan or post-plan), and cost estimation shows the projected cost delta of a run before apply.
- Projects group related workspaces for organization and permissioning, and run triggers let one workspace's successful apply queue a run in a downstream workspace.
- Team and permission scoping operates at the organization, project, and workspace levels, so access can be granted broadly or narrowly following least privilege.
- The private module registry and no-code modules let an organization publish approved, versioned modules that teams consume consistently.
- HCP Terraform can perform scheduled drift detection on workspaces, alerting when real infrastructure no longer matches the recorded state.
HashiCorp Terraform Authoring and Operations Professional exam tips
- Because half the exam is hands-on, practice in a real terminal against a live cloud provider until the core loops - init/plan/apply/destroy, state mv/rm, import blocks, moved blocks, and -refresh-only - are automatic; reading about them is not enough.
- Know the exact behavior of CLI flags cold: -detailed-exitcode codes (0/1/2), -replace vs the deprecated taint, -target as a recovery tool, plan -out then apply the saved plan, and -reconfigure vs -migrate-state on init.
- Master dynamic HCL: for expressions, for_each vs count and the state churn of converting between them, dynamic blocks, try/can, optional() object attributes, and the full variable precedence order.
- Be precise about modules and providers: the version argument works for registry/HCP sources but not git (use ref there), reusable modules should receive providers rather than declare them, and the .terraform.lock.hcl file must be committed and locked per platform.
- For objective 6 (multiple-choice), know HCP Terraform cold: the three run workflows, remote vs local execution, terraform vs env variables and variable sets, dynamic/OIDC credentials, and Sentinel/OPA enforcement levels (advisory / soft-mandatory / hard-mandatory).
Study guide FAQ
Is CertGrid a hands-on HashiCorp lab simulator?
No. The real HashiCorp Terraform Authoring and Operations Professional exam is a hands-on, performance-based lab exam. CertGrid provides MCQ-style readiness practice to help you check concepts, commands, troubleshooting choices, and weak domains before doing hands-on labs - it is not a live lab simulator.
What is the format and length of the Terraform Authoring and Operations Professional exam?
It is a four-hour, online-proctored exam delivered through Certiverse that combines hands-on lab scenarios - where you modify HCL and provision and manage real infrastructure in a pre-provisioned Linux environment - with multiple-choice questions. Objective 6 (HCP Terraform) is assessed with multiple-choice questions.
How is it scored and how long is it valid?
HashiCorp reports the result as pass/fail on a scaled score and does not publish a fixed passing percentage. The certification is valid for two years, and the exam typically includes one free retake after a failed attempt.
How is this different from the Terraform Associate exam?
The Associate (currently the 004 exam) is a 60-minute, multiple-choice exam covering foundational concepts and the core workflow. The Professional is a four-hour, largely hands-on exam that assumes Associate-level knowledge and tests deep authoring skill (dynamic HCL, modules, providers) and operational practice at scale, including HCP Terraform.
Do I need prior experience to take it?
Yes. HashiCorp expects deep knowledge of the configuration language, best practices, and the CLI, plus professional experience using Terraform with a cloud provider. It is not an entry-level certification, and hands-on practice is essential.
Does CertGrid's practice replace hands-on lab preparation?
No. CertGrid provides MCQ-style readiness practice across all six objectives to test the knowledge behind the tasks, but it is not a live terminal lab. Use it alongside real hands-on practice authoring and running Terraform against an actual cloud provider.
Related HashiCorp resources
- HashiCorp Terraform Authoring and Operations Professional practice exam
- HashiCorp practice exams
- Certification path
- HashiCorp Terraform Associate (004) study guide
- HashiCorp Vault Associate (003) study guide
- HashiCorp Vault Operations Professional study guide
- Certification exam guides & tips
- Pricing & plans
- FAQ