Free Terraform Associate (004) Practice Exam

Try 60 free Terraform Associate 004 practice questions with HCL, diagrams, Select TWO items, and explained answers. Continue with interactive IT Mastery practice.

Practise with 60 original Terraform Associate (004) questions covering Terraform 1.12 workflows, HCL, providers, modules, state, and HCP Terraform. Read the configuration and operational evidence before choosing an answer.

These are independent IT Mastery practice questions, not official HashiCorp questions, copied live-exam content, or exam dumps.

Start Question 1 · Review your attempt

How to use this practice set

  1. Record your answer before opening its explanation. Choose one answer unless the question explicitly asks for TWO.
  2. For timed practice, set your own 60-minute timer. This page does not run a timer, record answers, or calculate a score.
  3. Give each correctly answered question one point. For Select TWO, both choices must match, with no extra choice. Score out of 60 and mark correct guesses for review.
  4. Read the explanation and identify the evidence that rules out the strongest competing choice.

The 60-question length, topic weights, and item mix are editorial practice choices. HashiCorp describes an hour-long multiple-choice exam but does not specify a fixed question count or domain percentages in its public Associate 004 details. This set does not claim to reproduce the official assessment. Verify exam details .

Code blocks offer Wrap lines and Copy code. Wide tables scroll horizontally. Where a diagram appears, use Open full-size diagram or its Text description when you need more space.

Practice-set coverage

DomainIT Mastery practice weightQuestions in this set
Infrastructure as Code with Terraform6%4
Providers and State Fundamentals10%6
Core Terraform Workflow18%11
Configuration Language and Safeguards24%14
Module Interfaces and Reuse10%6
State Storage and Reconciliation12%7
Import and Operational Inspection8%5
HCP Terraform Collaboration12%7

Practice questions

Questions 1-25

Question 1

Topic: Configuration

An application team uses a provider-neutral Acme provider to create a service in a network owned by another team. The network name is stable, but its ID can change.

Requirements:

  • Each plan must resolve the current network ID through the provider API.
  • The application workspace may read networks and manage services.
  • It must not manage the network lifecycle and cannot access the network team’s state.

Provider schema:

  • data "acme_network" accepts name and exports id.
  • resource "acme_network" creates, updates, and deletes networks.
  • resource "acme_service" requires name and network_id.

Current draft:

resource "acme_service" "api" {
  name       = "orders-api"
  network_id = var.network_id
}

Which approach satisfies the requirements?

Options:

  • A. Import the network as acme_network.shared and reference its managed ID.

  • B. Read the network ID through terraform_remote_state and reference that output.

  • C. Maintain the network ID as an input variable and reference that value.

  • D. Add an acme_network data lookup by name and reference its ID.

Best answer: D

Explanation: A Terraform data source reads information about an externally owned object without taking responsibility for creating, updating, or deleting it. Looking up the network by its stable name retrieves the current ID during Terraform evaluation. Referencing that ID from acme_service.api.network_id also creates an implicit dependency, so Terraform obtains the network information before configuring the service.

Importing the network would bind it to a managed resource address and place its lifecycle within this workspace. A variable does not perform provider discovery, while remote-state access is unavailable here. The resulting managed infrastructure is the service, not the shared network.

  • Importing the network creates a managed state binding, conflicting with the external ownership boundary.
  • Reading remote state cannot work because the application workspace lacks access to the network team’s state.
  • Maintaining a variable does not query the provider API for the network’s current ID during each plan.

Question 2

Topic: Core Workflow

A previously initialized Terraform checkout changes one child module source:

# Previous declaration
module "network" {
  source = "git::ssh://git@example.com/acme/network.git?ref=v1.3.0"
}

# Current declaration
module "network" {
  source = "git::ssh://git@example.com/acme/network-v2.git?ref=v2.0.0"
}

Current context:

  • .terraform/modules contains the previous module.
  • Backend configuration is unchanged, and repository credentials are available.
  • Compatible newer releases exist for other modules and providers.
  • The team must retain unchanged dependency selections, check internal consistency, and review proposed changes before applying.

Which approach satisfies these requirements?

Options:

  • A. Run terraform validate, then terraform plan, then terraform init.

  • B. Run terraform init -upgrade, then terraform validate, then terraform plan.

  • C. Run terraform init, then terraform validate, then terraform plan.

  • D. Run terraform init -get=false, then terraform validate, then terraform plan.

Best answer: C

Explanation: Terraform must be reinitialized when a module source changes because the installed module under .terraform/modules no longer matches the configuration. A normal terraform init retrieves the module from its new source while retaining compatible selections for unchanged modules and providers. The provider lock file continues to govern selected provider versions; it does not lock module versions.

After initialization, terraform validate checks the configuration’s internal consistency with its dependencies available. terraform plan then evaluates the configuration using the actual backend, state, provider configuration, and remote APIs. Initialization prepares dependencies and the backend, but it does not prove apply-time provider permissions or produce an infrastructure-change plan.

The key distinction is that -upgrade intentionally reselects newer allowed dependencies, which conflicts with retaining unchanged selections.

  • Upgrade dependencies may select newer allowed versions of unrelated modules and providers, violating the requirement to preserve their selections.
  • Disable module retrieval prevents initialization from downloading the module at its changed source.
  • Check before initialization encounters a missing or mismatched module installation, and initializing afterward does not rerun the required checks.

Question 3

Topic: State Management

A CI runner uses a short-lived environment credential to access an encrypted remote backend restricted to the platform team. During terraform plan -out=tfplan:

  • service_token is marked sensitive and passed to a normal managed argument that the provider records in state.
  • bootstrap_password is a sensitive ephemeral variable passed only to a provider-supported write-only argument.
  • Provider TRACE logging records request payloads in trace.log.

The team commits tfplan to a private repository readable by all developers and attaches trace.log to a vendor support ticket. Which TWO statements correctly describe the security consequences? Select TWO.

Options:

  • A. Backend encryption also protects tfplan and trace.log, making their current repository and ticket permissions sufficient.

  • B. The service token can remain in tfplan, so repository access must be restricted separately from backend state access.

  • C. The bootstrap password is omitted from state and tfplan, but trace.log still requires protection because request payloads were logged.

  • D. Backend credential expiry makes tfplan and trace.log unreadable because both artifacts remain cryptographically tied to that credential.

  • E. The sensitive declaration omits the service token from state and tfplan, leaving only trace.log able to contain it.

Correct answers: B and C

Explanation: Terraform’s sensitive marker redacts normal presentation; it does not make a value non-persistent. Because the service token feeds a normal managed argument, the saved plan can retain it, and repository permissions apply independently of backend controls.

An ephemeral value passed through a provider-supported write-only argument is omitted from Terraform state and saved plans. That protection does not sanitize provider diagnostics, however. The stated TRACE behavior creates a separate exposure path for the bootstrap password. Exported plans and diagnostic logs therefore need their own access controls, retention rules, and secure sharing procedures. Expiration of the backend credential limits later backend access but does not make previously copied artifacts unreadable.

  • Backend protection inheritance fails because backend controls do not automatically protect copies stored in repositories or support systems.
  • Credential expiry does not encrypt or invalidate artifacts already copied outside the backend.
  • Sensitive means omitted is incorrect because sensitivity redacts display while ordinary managed values can remain in state and saved plans.

Question 4

Topic: HCP Terraform

An HCP Terraform organization offers the approved private-registry module network-stack version 2.4.0 as a no-code module.

  • An employee launches it in the Platform project with valid permitted inputs.
  • Execution is remote, auto-apply is enabled, and no policy or approval gate blocks the successful plan.
  • Later, version 3.0.0 becomes available and 2.4.0 is deprecated.

A workspace Change Request states:

Upgrade this deployment to module version 3.0.0.

The operator can manage the workspace configuration and runs. Which TWO statements correctly describe the deployment and follow-up? Select TWO.

Options:

  • A. The launch creates a dedicated workspace in Platform and auto-applies its successful remote run.

  • B. The operator fulfills the request by selecting 3.0.0 and completing the resulting workspace run.

  • C. The launch creates project-level state shared by all no-code workspaces in Platform.

  • D. The change request selects 3.0.0 and automatically queues the workspace upgrade run.

  • E. The launch creates a dedicated workspace in Platform but executes its first run locally.

Correct answers: A and B

Explanation: A no-code module makes an approved private-registry module available for self-service deployment. Each launch creates a dedicated HCP Terraform workspace in the selected project, with its own configuration, state, and run history. Because this workspace uses remote execution with auto-apply and has no blocking gate, its successful plan proceeds to apply remotely.

A workspace Change Request records and communicates an action item; it does not modify configuration or execute infrastructure changes. The authorized operator must select module version 3.0.0 and complete the resulting workspace run. Projects organize workspaces but do not merge their state.

  • Local first run conflicts with the workspace’s stated remote execution mode.
  • Shared project state is incorrect because each workspace maintains separate state despite belonging to the same project.
  • Automatic request action mistakes a tracking notice for executable configuration that changes versions and starts runs.

Question 5

Topic: Terraform Fundamentals

A team checks out a Terraform configuration with these provider requirements:

terraform {
  required_providers {
    random = {
      source  = "hashicorp/random"
      version = ">= 3.6.0, < 4.0.0"
    }
    time = {
      source  = "hashicorp/time"
      version = ">= 0.12.0, < 1.0.0"
    }
  }
}

The committed .terraform.lock.hcl contains a valid selection for random 3.6.3 but no entry for time. The registry offers:

ProviderAvailable versions
random3.6.3, 3.7.2, 4.0.0
time0.12.1, 0.13.1, 1.0.0

All listed versions support the current platform. The team runs terraform init without -upgrade.

Which TWO provider-selection outcomes will result? Select TWO.

Options:

  • A. Terraform installs time 1.0.0, selecting the newest release because no lock exists.

  • B. Terraform installs time 0.13.1, selecting the newest release within its constraint.

  • C. Terraform installs random 3.6.3, retaining the compatible locked selection.

  • D. Terraform stops provider selection because the lock file lacks a time entry.

  • E. Terraform installs random 3.7.2, selecting the newest allowed release despite the lock.

Correct answers: B and C

Explanation: Provider constraints define the allowed version range, while .terraform.lock.hcl records previously selected versions. A normal terraform init preserves the compatible locked selection of random 3.6.3 rather than automatically moving to 3.7.2. Because time has no lock entry, Terraform resolves its constraint, selects 0.13.1 as the newest listed version below 1.0.0, and adds that selection and its checksums to the lock file.

Running terraform init -upgrade would deliberately reconsider the existing random selection, but its configured constraint would still exclude 4.0.0.

  • Selecting random 3.7.2 would require terraform init -upgrade; plain initialization respects the compatible lock entry.
  • Selecting time 1.0.0 violates the configured < 1.0.0 constraint even though no selection is locked.
  • A missing provider entry does not block initialization; Terraform resolves the provider and extends the lock file.

Question 6

Topic: Modules

A root module composes network, policy, and service modules. The service resource uses var.subnet_id. Its API rejects creation until the policy module finishes enabling an account policy, but it does not consume any policy value. Policy metadata updates occur in place.

module "network" {
  source = "./modules/network"
}

module "policy" {
  source = "./modules/policy"
}

module "service" {
  source    = "./modules/service"
  subnet_id = module.network.subnet_id
}

Scroll sideways if needed. Open full-size diagram in a new tab

Text description

Three root child modules are shown. The Network module provides its subnet_id output to the Service module, while the Policy module has no edge to the Service module in the current graph.

Which change best establishes the required deployment ordering?

Options:

  • A. Keep the output reference and add depends_on = [module.policy] to module.service.

  • B. Keep the output reference and add depends_on = [module.service] to module.policy.

  • C. Keep the output reference and place module.service after module.policy in the file.

  • D. Keep the output reference and add depends_on = [module.network] to module.service.

Best answer: A

Explanation: Terraform derives an implicit dependency from module.network.subnet_id because the service resource ultimately consumes that value. No value reference expresses the separate requirement that the policy must be enabled first, so an explicit module-level depends_on is appropriate. It makes Terraform complete the policy module’s actions before operating on the service module. This dependency controls graph ordering; it does not itself request recreation of the service whenever policy metadata changes.

Configuration file order has no effect on Terraform’s dependency graph, and explicitly depending on the network alone merely duplicates an existing relationship.

  • File placement does not control execution order because Terraform builds its graph from references and explicit dependencies.
  • Network dependency is redundant and leaves the required policy prerequisite unexpressed.
  • Reverse dependency orders the policy after the service, contrary to the API requirement.

Question 7

Topic: Configuration

A team must compute a worker count once and reuse it across two resources. The policy must run in this order:

  • Minimum: 4 workers in production, otherwise 1.
  • Raise the requested count to the minimum if necessary.
  • Add 2 workers when burst mode is enabled.
  • Cap the final count at 8.

Current values are requested_workers = 7, environment = "production", and burst_mode = true.

locals {
  minimum_workers  = var.environment == "production" ? 4 : 1
  effective_workers = ???
}

resource "terraform_data" "pool" {
  input = local.effective_workers
}

resource "terraform_data" "monitor" {
  input = local.effective_workers
}

Which expression should replace ??? to implement the policy for all nonnegative requested counts?

Options:

  • A. min(8, max(var.requested_workers, local.minimum_workers) + (var.burst_mode ? 2 : 0))

  • B. max(local.minimum_workers, min(8, var.requested_workers)) + (var.burst_mode ? 2 : 0)

  • C. min(8, max(var.requested_workers, local.minimum_workers + (var.burst_mode ? 2 : 0)))

  • D. min(8, max(var.requested_workers + (var.burst_mode ? 2 : 0), local.minimum_workers))

Best answer: A

Explanation: Terraform evaluates nested expressions from the inside outward. Production sets local.minimum_workers to 4. The requested value is first raised to that minimum with max(7, 4), producing 7. Burst mode then adds 2, producing 9, and min(8, 9) caps the result at 8.

Keeping this calculation in one local prevents the pool and monitoring resources from implementing separate versions of the policy. Moving the burst addition inside max is not equivalent because a production request below the minimum would not receive the full burst increment after the minimum is enforced.

  • Adding burst before enforcing the minimum would produce 4 for a production request of 1, although the ordered policy requires 6.
  • Capping before adding burst allows the final result to exceed 8; the current values would produce 9.
  • Adding burst only to the minimum changes the floor rather than the selected worker count; the current values would produce 7.

Question 8

Topic: Infrastructure Maintenance

A team will adopt an existing AWS VPC into Terraform. The VPC ID is vpc-0123456789abcdef0, it exists only in us-east-1, and its settings exactly match this configuration. AWS VPC imports require the VPC ID, not an ARN.

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  alias  = "east"
  region = "us-east-1"
}

provider "aws" {
  alias  = "west"
  region = "us-west-2"
}

resource "aws_vpc" "analytics" {
  provider             = aws.east
  cidr_block           = "10.42.0.0/16"
  enable_dns_support   = true
  enable_dns_hostnames = false
  tags = { Name = "analytics" }
}

____

The team will review a saved plan showing one import and no other changes before applying it:

terraform init
terraform plan -out=adopt.tfplan
# Team reviews adopt.tfplan
terraform apply adopt.tfplan

Which HCL fragment should replace ____?

Options:

  • A.

    import {
      to       = aws_vpc.analytics
      id       = "arn:aws:ec2:us-east-1:123456789012:vpc/vpc-0123456789abcdef0"
      provider = aws.east
    }
    
  • B.

    import {
      to       = aws_vpc.analytics["vpc-0123456789abcdef0"]
      id       = "vpc-0123456789abcdef0"
      provider = aws.east
    }
    
  • C.

    import {
      to       = aws_vpc.analytics
      id       = "vpc-0123456789abcdef0"
      provider = aws.east
    }
    
  • D.

    import {
      to       = aws_vpc.analytics
      id       = "vpc-0123456789abcdef0"
      provider = aws.west
    }
    

Best answer: C

Explanation: A declarative import block identifies the existing remote object, its intended Terraform resource address, and optionally the provider configuration used to inspect it. Here, the raw VPC ID must map to the singleton address aws_vpc.analytics through the east-region provider alias. Because the resource configuration matches the existing VPC, the saved plan should propose an import rather than creation or argument updates. After review, applying that saved plan records the binding in state without creating another VPC.

The import destination must exactly match a configured resource instance; an import identifier is not automatically an instance key.

  • ARN identifier fails because this resource’s import operation requires the raw VPC ID.
  • West provider searches the region where the VPC does not exist.
  • Indexed destination names a for_each instance, but the configured resource is a singleton.

Question 9

Topic: Core Workflow

An initialized Terraform CLI working directory uses CLI workspaces. terraform workspace show returns default, but the temporary environment is owned by temp-green.

temp-green state:

  • module.app.aws_instance.web and aws_cloudwatch_log_group.temp are managed and match configuration.
  • data.aws_vpc.shared reads a platform-owned VPC that must remain.
  • A manually created debug instance is not tracked and must remain.

Provider credentials can read the VPC and delete both managed objects. The configuration remains unchanged. A reviewer must inspect the exact teardown plan before that same plan is applied.

Which procedure performs the required teardown?

Options:

  • A. Select temp-green; save and review a normal plan; apply that saved plan.

  • B. Select temp-green; save and review a -destroy -target=module.app plan; apply that saved plan.

  • C. Select temp-green; save and review a -refresh-only plan; apply that saved plan.

  • D. Select temp-green; save and review a full -destroy plan; apply that saved plan.

Best answer: D

Explanation: Terraform destroy planning is scoped to the selected workspace’s state. After selecting temp-green, terraform plan -destroy -out=teardown.tfplan proposes deletion of every managed resource recorded there. Applying teardown.tfplan executes the reviewed plan. The VPC data source may be read during evaluation, but Terraform does not own or destroy its remote object. The manually created debug instance is also excluded because it has no state binding.

A normal plan preserves resources that still match unchanged configuration. Refresh-only mode updates state observations without proposing destruction, while targeting module.app omits the independently managed log group.

  • Normal plan proposes no teardown because the managed objects still match the unchanged configuration.
  • Refresh-only plan reconciles state with remote observations but does not delete managed infrastructure.
  • Targeted destroy limits scope to module.app, leaving the root log group managed and present.

Question 10

Topic: Infrastructure as Code

A platform team is handing an HCP Terraform workspace in local execution mode to operations.

  • HCP Terraform holds the authoritative state, but only one platform engineer has workspace access.
  • The repository contains the .tf files and a Terraform 1.12 constraint, but ignores the current .terraform.lock.hcl.
  • A newer provider release satisfies the configured constraint.
  • HCP and provider credentials currently come from the engineer’s shell.

Operations must retain the same managed objects, avoid provider upgrades during handoff, and keep credentials outside the repository. The change record must identify configuration and version selections, state ownership and access, and a verification plan.

Which handoff approach satisfies these requirements?

Options:

  • A. Commit the current lock file and tag the revision; transfer workspace access, use terraform login for HCP and provider authentication, run plan, and record the results.

  • B. Commit the current lock file and tag the revision; transfer workspace access, issue separate runtime credentials, run init and plan, and record the results.

  • C. Regenerate the lock file with init -upgrade and tag the revision; transfer workspace access, issue separate runtime credentials, run plan, and record the results.

  • D. Commit the current lock file and tag the revision; transfer workspace access, store provider credentials as HCP workspace variables, run a local plan, and record the results.

Best answer: B

Explanation: An auditable handoff must preserve both Terraform’s recorded intent and its runtime prerequisites. Committing .terraform.lock.hcl records the selected provider versions and checksums, while the tagged configuration revision identifies the reviewed code. Operations also need authorized access to the existing HCP Terraform state so Terraform retains its bindings to the managed objects. Because execution is local, the execution host must receive separate HCP and target-provider credentials through an approved runtime channel. A normal terraform init respects the compatible locked selections, and a fresh plan verifies the transferred operating context. Using init -upgrade would deliberately reconsider provider selections rather than preserve them.

  • Upgrade during handoff fails because init -upgrade can select the newer allowed provider release.
  • Single login assumption fails because terraform login authenticates HCP Terraform, not the target infrastructure provider.
  • Workspace variable reliance fails because HCP workspace variables are not supplied to Terraform runs performed in local execution mode.

Question 11

Topic: Configuration

A CI job rotates a database password. It supplies TF_VAR_password and changes TF_VAR_rotation_id only for an approved rotation.

Provider-neutral resource schema:

  • password is conventional, sensitive, and stored in state.
  • password_wo is write-only, accepts ephemeral values, and is not stored in state or plans.
  • password_wo_version is stored in state; changing it triggers a password update.

Assume the provider is installed and configured.

terraform {
  required_version = ">= 1.12.0"
}

variable "password" {
  type      = string
  sensitive = true
  ephemeral = true
}

variable "rotation_id" {
  type = string
}

resource "training_database" "orders" {
  name = "orders"

  ____
}

Which fragment should replace ____?

Options:

  • A. password_wo = var.password and password_wo_version = sha256(var.password)

  • B. password_wo = var.password and password_wo_version = "1"

  • C. password = var.password and password_wo_version = var.rotation_id

  • D. password_wo = var.password and password_wo_version = var.rotation_id

Best answer: D

Explanation: A provider-supported write-only argument is an ephemeral context, so it can consume the ephemeral password without persisting it in state or saved plans. Terraform still needs a durable, nonsecret value to detect an intended rotation. Assigning the CI-managed rotation ID to the companion version argument records that signal in state and causes an update when the ID changes.

Marking a conventional argument sensitive provides display redaction, not non-persistence. Also, an expression derived from an ephemeral value remains ephemeral, so hashing the password does not make it suitable for a persisted version argument. The version trigger should be stable between approved rotations and independent of the secret itself.

  • Conventional password field cannot consume the ephemeral value and would ordinarily persist its value in state.
  • Fixed version value cannot signal later rotations after its initial value has been recorded.
  • Password-derived version remains ephemeral because ephemerality propagates through expressions, making it invalid for the persisted trigger.

Question 12

Topic: State Management

A company manages network and application resources from one root configuration in one HCP Terraform workspace.

Requirements:

  • Only the network team may access network state or release network changes.
  • The application team releases daily without waiting for monthly network approvals.
  • The application requires an approved subnet_id output but no other network state access.
  • Existing resources must remain bound without recreation.
  • All runs continue using HCP Terraform remote execution.

Both teams may reuse the same approved modules. Which approach satisfies these requirements?

Options:

  • A. Split into separate roots and HCP workspaces, migrate each binding, and authorize tfe_outputs access to the network output.

  • B. Split into separate roots and HCP workspaces, migrate each binding, and grant terraform_remote_state access to the network output.

  • C. Keep one root and HCP workspace, separate resources into child modules, and use VCS approvals with targeted team runs.

  • D. Split into separate roots and HCP workspaces, migrate each binding, and use a run trigger to pass the network output.

Best answer: A

Explanation: An HCP Terraform workspace is a state, permission, and run boundary. Separate network and application roots in separate workspaces allow independent release schedules and workspace-specific team access. Existing state bindings must be migrated carefully so Terraform does not propose recreating managed objects. The network workspace can publish subnet_id as a root output, and authorized tfe_outputs access lets the application retrieve that output without requiring access to the complete network state snapshot. Approved modules can still be reused across both roots because module reuse does not require shared state.

Child modules organize configuration, but they do not isolate state or workspace permissions.

  • terraform_remote_state requires access to the complete network state snapshot, violating the output-only access requirement.
  • A run trigger queues a downstream run after an apply; it neither transfers outputs nor grants access to them.
  • Child modules and targeted runs retain shared state, permissions, run locking, and release coupling within one workspace.

Question 13

Topic: HCP Terraform

Two HCP Terraform workspaces use remote execution. network-prod declares a root output named subnet_id, which app-prod needs during planning.

Scroll sideways if needed. Open full-size diagram in a new tab

Text description

The network-prod workspace produces subnet_id, and app-prod requires both notification of a successful network apply and access to that value.

Requirements:

  • A successful network-prod apply must queue an app-prod run.
  • The consumer must read the current output without access to the producer’s complete state.
  • App owners must manually approve each consumer apply.

Which approach satisfies all requirements?

Options:

  • A. Create a producer-to-consumer run trigger; use terraform_remote_state with producer state access; grant app owners apply permission.

  • B. Create a producer-to-consumer run trigger; use tfe_outputs with output-read authorization; grant app owners apply permission.

  • C. Create a producer-to-consumer run trigger; use tfe_outputs with output-read authorization; enable run-trigger automatic apply.

  • D. Use tfe_outputs with output-read authorization; grant app owners apply permission; rely on the data reference to queue runs.

Best answer: B

Explanation: An HCP Terraform run trigger coordinates execution but does not transfer output values. The producer-to-consumer trigger queues an app-prod run after a successful network-prod apply. During that run, the tfe_outputs data source can retrieve subnet_id using an identity authorized to read the producer’s outputs, without requiring access to its complete state snapshot. Apply permission is separate from both triggering and output access, so app owners need that permission to approve the consumer apply manually.

A Terraform data reference creates a configuration dependency but does not independently queue another workspace’s run.

  • terraform_remote_state requires access to the complete state snapshot, violating the stated access restriction.
  • Run-trigger automatic apply bypasses the required manual approval by app owners.
  • A tfe_outputs reference retrieves values during a run but does not initiate that run when the producer changes.

Question 14

Topic: Core Workflow

A team previously initialized a directory with the default local backend. Its terraform.tfstate contains all current managed-object bindings. The destination bucket is empty, and the operator can read the local state and write to the bucket.

The team adds this configuration:

terraform {
  backend "s3" {
    bucket = "acme-tf-state"
    key    = "network/prod.tfstate"
    region = "us-east-1"
  }
}

The destination must become the active backend while retaining the existing bindings. Which fragment completes the command?

terraform ____

Options:

  • A. init -upgrade

  • B. init -backend=false

  • C. init -migrate-state

  • D. init -reconfigure

Best answer: C

Explanation: Backend migration is required because the existing state remains local and the destination has no copied snapshot. terraform init -migrate-state initializes the new backend and offers to copy the current state, preserving resource-address-to-object bindings without changing infrastructure. After confirmation, Terraform records the new backend configuration for subsequent operations.

Reconfiguration alone is appropriate when Terraform should accept different backend settings without migrating the source snapshot, such as when an administrator has already copied and verified the state. That condition does not apply here.

  • Reconfigure only accepts the new backend settings but does not migrate the existing local snapshot into the empty destination.
  • Upgrade dependencies selects newer allowed provider or module versions rather than resolving the required state transfer.
  • Disable backend initialization prevents Terraform from preparing the destination backend at all.

Question 15

Topic: Terraform Fundamentals

A root module uses the AWS provider, which requires a region in this environment. Credentials are available, but no environment variable supplies a default region. The archive child module expects its default AWS configuration from the caller.

terraform {
  required_providers {
    aws = { source = "hashicorp/aws" }
  }
}

provider "aws" {
  alias  = "east"
  region = "us-east-1"
}

provider "aws" {
  alias  = "west"
  region = "us-west-2"
}

resource "aws_s3_bucket" "logs" {
  bucket = "example-logs-4831"
}

module "archive" {
  source    = "./modules/archive"
  providers = { aws = aws.west }
}

terraform plan reports that the implied default AWS configuration lacks a region. The bucket must use aws.east, the child module must continue using aws.west, and no unaliased provider configuration may be added.

Which change satisfies these requirements?

Options:

  • A. Map the module to aws.east; keep the bucket implicit and both aliases.

  • B. Set the bucket’s provider to aws.west; keep the module mapped west.

  • C. Set the bucket’s provider to aws.east; keep the module mapped west.

  • D. Add an unaliased east provider; keep the bucket implicit and module mapped west.

Best answer: C

Explanation: When every configuration for a provider is aliased, Terraform creates an implied empty default configuration. A resource without a provider meta-argument selects that default, not one of the aliases automatically. Because the implied AWS configuration has no region, planning fails.

Adding provider = aws.east to the bucket explicitly associates it with the required east configuration. The providers map on the module call applies only inside that child module, so its existing mapping can continue supplying aws.west as the child’s default AWS configuration.

Provider aliases must be selected explicitly when no usable default configuration exists.

  • Adding an unaliased east provider would make implicit selection work, but it violates the requirement prohibiting an unaliased configuration.
  • Remapping the child module does not affect the root bucket and would also move the child away from west.
  • Selecting aws.west explicitly resolves the missing default configuration but places the bucket in the wrong region.

Question 16

Topic: Configuration

A reusable module must normalize settings while leaving an omitted numeric timeout nullable.

variable "settings" {
  type    = ____
  default = {}
}

output "normalized" {
  value = var.settings
}

Two callers pass:

module "alpha" {
  source = "./settings"
  settings = {
    mode   = null
    limits = { retries = 5 }
  }
}

module "beta" {
  source   = "./settings"
  settings = {}
}

Required normalized values:

  • alpha: mode "standard", retries 5, timeout null
  • beta: mode "standard", retries 3, timeout null

Which type constraint should replace ____?

Options:

  • A.

    object({
      mode = optional(string, "standard")
      limits = optional(object({
        retries = optional(number, 3)
        timeout = optional(number)
      }), {})
    })
    
  • B.

    object({
      mode = optional(string, "standard")
      limits = optional(object({
        retries = optional(number, 3)
        timeout = optional(number)
      }))
    })
    
  • C.

    object({
      mode = optional(string, "standard")
      limits = optional(object({
        retries = optional(number, 3)
        timeout = optional(number, 30)
      }), {})
    })
    
  • D.

    object({
      mode = optional(string)
      limits = optional(object({
        retries = optional(number, 3)
        timeout = optional(number)
      }), {})
    })
    

Best answer: A

Explanation: Terraform applies optional object-attribute defaults from the outer object inward. optional(string, "standard") replaces both an omitted mode and an explicit null mode with "standard". Giving the optional limits object a default of {} ensures that its nested defaults are evaluated even when callers omit the entire object. The nested retries default therefore becomes 3 for beta, while alpha preserves its supplied value of 5. An optional attribute without an explicit default uses a typed null value, so the omitted timeout remains null for both callers.

The key distinction is that a non-null optional default replaces explicit null, whereas an optional attribute with no stated default remains nullable.

  • Omitting the {} default makes beta.settings.limits null, so its nested retries default is not produced.
  • Omitting the mode default leaves the explicit null from alpha and the omitted mode from beta as null.
  • Assigning timeout a default of 30 replaces the required null value whenever callers omit that attribute.

Question 17

Topic: Modules

A platform team publishes each reviewed network module release to its HCP Terraform private registry. Application teams use independently released root repositories.

  • CI can authenticate to HCP Terraform and reach its module download endpoints.
  • CI cannot reach the platform team’s Git server.
  • Each environment has a different network CIDR.
  • The repository is a clean checkout before initialization.
variable "network_cidr" {
  type = string
}

module "network" {
  source  = "app.terraform.io/acme/network/aws"
  version = "~> 3.2"
  cidr    = var.network_cidr
}

Which TWO statements correctly describe this configuration and its first terraform init? Select TWO.

Options:

  • A. The version argument constrains the Terraform CLI release.

  • B. Each root module supplies its CIDR through the declared module input.

  • C. Terraform records the selected module release in .terraform.lock.hcl.

  • D. Initialization can install the module without reaching the Git server.

  • E. Edits in a sibling ../network directory alter the installed module.

Correct answers: B and D

Explanation: A private registry source fits independently released repositories because terraform init authenticates to HCP Terraform, resolves the module version constraint, and downloads the released package. The consuming CI system does not need access to the module’s underlying Git repository when it can reach the registry and download endpoints.

The module remains reusable source code, while network_cidr is environment data supplied by each root module through the declared interface. Terraform’s dependency lock file records provider selections and checksums, not remote module versions. The version argument in a module block constrains the module release; terraform.required_version constrains the CLI. Local directory edits matter only when the module uses a local path source.

  • Dependency lock file tracks provider packages, not the selected release of a remote module.
  • CLI version constraint belongs in terraform.required_version, not a module block’s version argument.
  • Sibling directory edits affect a local path source, whereas this registry source uses a downloaded release.

Question 18

Topic: Infrastructure Maintenance

An initialized root module uses a local child module:

module "storage" {
  source   = "./modules/storage"
  for_each = {
    logs  = "acme-logs-prod"
    media = "acme-media-prod"
  }
  bucket_name = each.value
}

The child module contains:

variable "bucket_name" {
  type = string
}

resource "aws_s3_bucket" "this" {
  bucket = var.bucket_name
}

The existing bucket acme-logs-prod must be managed by the logs module instance. The provider imports an S3 bucket using its bucket name. In a Bash shell, which command imports the bucket into that module instance?

Options:

  • A. terraform import 'module.storage["media"].aws_s3_bucket.this' acme-logs-prod

  • B. terraform import 'module.storage["acme-logs-prod"].aws_s3_bucket.this' acme-logs-prod

  • C. terraform import 'module.storage["logs"].aws_s3_bucket.this["logs"]' acme-logs-prod

  • D. terraform import 'module.storage["logs"].aws_s3_bucket.this' acme-logs-prod

Best answer: D

Explanation: A module using for_each has one module instance for each map key. Its address therefore includes the key after the module name: module.storage["logs"]. The child resource address follows that module instance. Because aws_s3_bucket.this does not itself use count or for_each, it has no additional instance key.

Single quotes preserve the brackets and embedded double quotes when Bash passes the address to Terraform. The remote identifier remains the provider-defined bucket name, acme-logs-prod. Import then binds that remote object to the specified configuration address; it does not choose an instance based on the bucket name.

  • Using media selects the other declared module instance, so it would associate the bucket with the wrong configuration.
  • Using the bucket name as the module key fails because module instances are keyed by logs and media, not map values.
  • Adding a resource key is invalid because the child resource is singular within each module instance.

Question 19

Topic: Core Workflow

A CI job runs from the repository root:

.
├── main.tf
├── outputs.tf
└── modules
    └── network
        ├── main.tf
        └── variables.tf

The gate must inspect root and nested Terraform files, display line-level formatting differences, and fail if formatting is noncanonical without rewriting files. Configuration validation runs in a separate job.

terraform fmt ____

Which fragment completes the command?

Options:

  • A. -check -diff -no-color

  • B. -check -recursive -diff

  • C. -check -recursive -no-color

  • D. -recursive -diff -no-color

Best answer: B

Explanation: terraform fmt normally rewrites files into Terraform’s canonical format. The -check flag changes this to check-only behavior and returns a nonzero exit status when formatting changes are needed. -recursive extends processing into nested directories, including the child module. -diff prints the formatting differences for CI review.

This gate checks presentation rather than full configuration validity. A formatting failure means one or more files are not canonically formatted; passing it does not prove that references, module inputs, or other configuration semantics are valid. Those concerns belong to terraform validate after initialization.

  • Omitting -check allows terraform fmt to rewrite repository files.
  • Omitting -recursive leaves the nested network module unchecked.
  • Omitting -diff detects formatting failures but does not display the requested line-level differences.

Question 20

Topic: Configuration

After a successful apply, a CI script runs terraform output -json. The consumer expects one JSON object containing all root outputs and their metadata. Secrets may exist in protected process memory, but must not enter ordinary logs.

variable "api_token" {
  type      = string
  sensitive = true
}

output "endpoint" {
  value = "api.internal.example:443"
}

output "deployment" {
  value = {
    zones     = toset(["zone-a", "zone-b"])
    api_token = var.api_token
  }
  sensitive = true
}

Which TWO statements describe the command’s output? Select TWO.

Options:

  • A. Can be replaced by terraform output -raw deployment with identical structure

  • B. Includes api_token as plaintext in the machine-readable JSON document

  • C. Represents zones as a JSON object keyed by each set member

  • D. Produces an object keyed by output names, including metadata for each

  • E. Replaces api_token with a redaction marker while retaining other fields

Correct answers: B and D

Explanation: Running terraform output -json without naming an output returns a top-level JSON object keyed by root output names. Each entry contains metadata such as sensitive, type, and value. Terraform converts collection values into compatible JSON representations, so a set appears as a JSON array.

The sensitive setting redacts values in normal human-readable output, but machine-readable -json and -raw modes can reveal them. Therefore, the JSON document, process output, and any captured logs require secret-safe handling. The -raw form is intended for a named scalar string, number, or boolean, not a nested object.

  • Redaction marker does not apply to machine-readable JSON output, which can expose sensitive values.
  • Set representation is an array because JSON has no distinct set type.
  • Raw object output fails because -raw supports scalar values rather than nested structures.

Question 21

Topic: State Management

A team uses Terraform CLI with a remote backend. A provider-neutral training_service.app resource creates a distinct remote object on every create; retries do not adopt an existing object.

An apply reports:

training_service.app: Creation complete [id=svc-7842]

Error: Failed to save state
Error saving state: backend unavailable
The state has been saved to "errored.tfstate".

Recovery facts:

  • The backend’s last known state has lineage L1 and serial 41.
  • errored.tfstate has lineage L1, serial 42, and binds the resource to svc-7842.
  • The provider API confirms that svc-7842 exists.
  • Multiple operators can run Terraform.

Which approach should the team use to recover safely?

Options:

  • A. Pause writers and secure the artifact; restore the backend, push the artifact immediately, then pull and compare the resulting state with infrastructure.

  • B. Pause writers and secure the artifact; restore the backend, verify its latest state is the predecessor, push the artifact normally, then pull and plan.

  • C. Pause writers and secure the artifact; restore the backend, verify its latest state, run a refresh-only apply to discover the binding, then plan.

  • D. Pause writers and secure the artifact; restore the backend, verify its latest state, rerun the apply to reconstruct the missing binding, then plan.

Best answer: B

Explanation: When infrastructure changes succeed but backend persistence fails, errored.tfstate may be the only state containing the new binding. Treat it as sensitive, freeze other writers, restore backend access, and pull the current authoritative snapshot. Compare lineage, serial, bindings, and the observed remote object. If the backend still contains the verified predecessor, push the recovery artifact using normal lineage and serial safeguards. Then pull state again and run a fresh normal plan.

If the backend advanced, stop and reconcile instead of forcing an overwrite. Refresh-only operations cannot discover an untracked object without its provider identity, and repeating the apply could create another object.

  • Push before verification risks overwriting a state snapshot that advanced or was recovered while the backend was unavailable.
  • Refresh-only recovery refreshes tracked bindings but cannot discover and bind the newly created untracked object.
  • Repeat the apply may create a second object because Terraform still sees no binding in the backend state.

Question 22

Topic: HCP Terraform

An HCP Terraform project contains api and worker workspaces. Both receive these non-priority Terraform variable sets:

  • Organization set platform-defaults: region = "us-east-1"
  • Project set runtime-defaults: log_level = "warn"

The api workspace defines region = "eu-west-1"; worker has no workspace value for region. Workspace variables override non-priority variable sets.

An administrator changes platform-defaults.region to us-west-2. Both workspaces start new runs afterward. Which TWO variable resolutions occur? Select TWO.

Options:

  • A. worker resolves region as unset.

  • B. api resolves region to us-west-2.

  • C. api resolves region to eu-west-1.

  • D. worker resolves region to us-east-1.

  • E. worker resolves region to us-west-2.

Correct answers: C and E

Explanation: A non-priority variable set supplies shared values while allowing deliberate workspace overrides. The api workspace keeps eu-west-1 because its workspace-specific Terraform variable has higher precedence. The worker workspace has no override, so its next run receives the updated shared value, us-west-2.

Variable set values are not copied into each workspace when the set is attached. Subsequent runs use the applicable current values. Updating a variable set also does not immediately change infrastructure; the new value influences planning and applying in later runs. A priority variable set would have different precedence, but neither attached set is priority here.

  • The shared value cannot replace api’s higher-precedence workspace value.
  • The original worker value is not retained as an attachment snapshot.
  • Lacking a workspace value does not unset region; the attached set supplies it.

Question 23

Topic: Infrastructure as Code

A team provisions AWS artifact buckets through a child module. Each environment has separate configuration and state. The current module accepts store_name and location and returns store_id.

resource "aws_s3_bucket" "this" {
  bucket = var.store_name
}

A new environment must create a Google Cloud Storage bucket while leaving the AWS bucket and state unchanged. The team may change the module source and provider configuration, but must preserve the module interface and its initialize-plan-approval-apply workflow.

aws_s3_bucket is AWS-specific. Google requires google_storage_bucket with name and location. The new bucket must be created by the reviewed Terraform apply; out-of-band creation is not permitted. No Google bucket currently exists, and neither provider supports converting state between these resource types.

Which approach satisfies the requirements?

Options:

  • A. Create a Google-specific module, create the bucket outside Terraform, import it into new state, and apply a reviewed plan.

  • B. Copy the AWS module unchanged, map its local aws provider name to Google, and run the existing workflow with new state.

  • C. Create a Google-specific module with the same contract, configure its Google provider, and run the existing workflow with new state.

  • D. Create a Google-specific module, copy the AWS state, run state replace-provider, and apply a reviewed plan against the copied state.

Best answer: C

Explanation: Terraform’s workflow, review process, and module interfaces are portable across providers, but provider configurations and resource definitions are not. The Google implementation should declare and use the Google provider, define google_storage_bucket with its required arguments, and expose the same variables and store_id output expected by callers. Because this is a new environment, it should begin with separate state and proceed through initialization, plan review, approval, and apply.

Changing a provider mapping does not make Google support an AWS resource type. State replacement changes a provider source association; it does not translate an AWS object into a Google object. Import is intended to bind an existing object, not create the required bucket through Terraform.

  • Provider remapping fails because a Google provider does not implement the AWS-specific aws_s3_bucket resource schema.
  • State source replacement cannot convert the tracked AWS bucket into a different resource type on another platform.
  • Import workflow assumes an existing bucket and bypasses the requirement for Terraform to create the new object.

Question 24

Topic: Configuration

An illustrative provider-neutral configuration has these semantics:

  • The tier data source returns enabled during planning because var.tier is known.
  • The service provider assigns endpoint only after creating the service.
  • The binding depends on the service through service_id.
data "catalog_tier" "selected" {
  name = var.tier
}

resource "managed_service" "api" {
  tier = var.tier

  lifecycle {
    precondition {
      condition     = data.catalog_tier.selected.enabled
      error_message = "The selected tier is disabled."
    }

    postcondition {
      condition     = startswith(self.endpoint, "https://")
      error_message = "The endpoint must use HTTPS."
    }
  }
}

resource "service_binding" "consumer" {
  service_id = managed_service.api.id
}

Which TWO statements correctly describe a normal plan-and-apply workflow? Select TWO.

Options:

  • A. A disabled tier fails during planning, so neither resource receives a create request.

  • B. A non-HTTPS endpoint produces a warning, allowing the dependent binding to be created.

  • C. A disabled tier is checked only during apply because its value comes from a data source.

  • D. A non-HTTPS endpoint is detected during planning, before the provider creates the service.

  • E. A non-HTTPS endpoint can leave the service created while blocking creation of the dependent binding.

Correct answers: A and E

Explanation: A condition must be placed where its required evidence is available. The tier lookup completes during planning, so the resource precondition can block the plan before Terraform calls either resource provider. The endpoint is unknown until the service has been created, making a resource postcondition appropriate. If that postcondition fails, Terraform reports an apply failure and blocks operations for dependent resources. Terraform does not automatically roll back the service that was already created.

A failed check assertion would produce a warning, but lifecycle preconditions and postconditions are blocking conditions.

  • Data-source deferral is incorrect because the supplied semantics state that the lookup completes during planning.
  • Early endpoint check is impossible because the provider does not assign the endpoint until service creation.
  • Warning-only behavior describes a failed check assertion, not a failed resource postcondition.

Question 25

Topic: Core Workflow

A teammate must execute a Terraform 1.12 change locally from a clean repository checkout.

  • .terraform.lock.hcl is committed, and dependency upgrades are not authorized.
  • The remote backend, environment inputs, backend credentials, and provider credentials are configured.
  • The checkout has no .terraform directory.
  • require_approval shows its input to an authorized reviewer and succeeds only after approval.

Complete the script so the exact reviewed proposal is executed.

set -euo pipefail

____
terraform show -no-color tfplan | require_approval
terraform apply -input=false tfplan

Which fragment should replace ____?

Options:

  • A.

    terraform validate
    terraform plan -input=false -out=tfplan
    
  • B.

    terraform init -input=false
    terraform plan -input=false -out=tfplan
    
  • C.

    terraform init -input=false -upgrade
    terraform plan -input=false -out=tfplan
    
  • D.

    terraform init -input=false -backend=false
    terraform plan -input=false -out=tfplan
    

Best answer: B

Explanation: A clean checkout must be initialized before planning. Normal terraform init prepares the backend, installs modules and providers, and respects compatible selections in .terraform.lock.hcl. The -out=tfplan argument creates a saved plan rather than merely displaying a speculative plan. terraform show tfplan then renders that saved artifact for approval, and terraform apply tfplan executes it without creating a new plan.

Saved plans can contain sensitive data and are tied to their planning context, so they must be protected and applied from the intended environment. Dependency upgrades should also be performed separately when they are explicitly authorized.

  • Unauthorized upgrade uses -upgrade, which can select newer provider versions allowed by the constraints instead of preserving locked selections.
  • Backend disabled prevents initialization of the configured remote backend needed to access existing state during planning.
  • Initialization omitted runs validation and planning before the clean checkout has installed dependencies or initialized its backend.

Questions 26-50

Question 26

Topic: Terraform Fundamentals

All blocks are in one working directory. Each bucket is created in the region of its assigned AWS provider configuration. The primary bucket must use us-east-1, and the replica must use us-west-2. Which ordered pair should replace the blanks, from primary to replica?

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

provider "aws" {
  alias  = "west"
  region = "us-west-2"
}

resource "aws_s3_bucket" "primary" {
  provider = ____
  bucket   = "exam-primary-48291"
}

resource "aws_s3_bucket" "replica" {
  provider = ____
  bucket   = "exam-replica-48291"
}

Options:

  • A. Set primary to aws.east and replica to aws.west.

  • B. Set primary to aws and replica to aws.

  • C. Set primary to aws and replica to aws.west.

  • D. Set primary to aws.west and replica to aws.

Best answer: C

Explanation: A provider configuration without an alias is the default instance and is referenced by its local provider name, aws. An aliased configuration uses the address aws.<alias>, making the western instance aws.west. The provider meta-argument selects the configured instance; the recommended syntax uses these unquoted references.

Terraform combines configuration files in the working directory into one module. Filenames and declaration order do not determine which provider instance a resource uses. A resource normally uses its provider type’s default configuration unless an aliased instance is selected explicitly. Reversing the references would create the buckets in the opposite regions.

  • Invented east alias fails because no provider configuration declares alias = "east".
  • Reversed references assigns the primary bucket to the western configuration and the replica to the default eastern configuration.
  • Default for both places both buckets in us-east-1, so the replica does not use the western configuration.

Question 27

Topic: Modules

A root module creates one service module instance per service name. The monitor module must receive only the api and web endpoints.

locals {
  service_names = toset(["api", "worker", "web"])
  monitored     = toset(["api", "web"])
}

module "service" {
  source   = "./service"
  for_each = local.service_names
  name     = each.key
}

module "monitor" {
  source    = "./monitor"
  endpoints = ____
}

The service module exports endpoint as a string. The monitor module declares endpoints as map(string) and requires service names as its keys.

Which expression should replace ____?

Options:

  • A. { for key in local.monitored : module.service[key].endpoint => key }

  • B. { for key in local.monitored : key => module.service[key] }

  • C. { for key, instance in module.service : key => instance.endpoint }

  • D. { for key in local.monitored : key => module.service[key].endpoint }

Best answer: D

Explanation: A module using for_each produces a collection keyed by the values supplied to for_each. An individual instance is therefore accessed as module.service[key], and its output as module.service[key].endpoint.

A map comprehension over local.monitored selects only the required instances. Using each service name as the result key and its endpoint output as the result value produces the required map(string) with api and web entries. Iterating over the entire module collection would also include worker, while returning module instances would not satisfy the required value type.

  • Iterating over every service instance includes the unmonitored worker entry.
  • Using endpoints as map keys reverses the consumer’s required name-to-endpoint mapping.
  • Returning module instances produces objects rather than the required string values.

Question 28

Topic: Configuration

A provider-neutral resource schema accepts repeatable rule blocks containing port and protocol arguments. The configuration must create one managed resource and generate one nested block for every input element.

variable "rules" {
  type = list(object({
    port     = number
    protocol = string
  }))

  default = [
    { port = 80, protocol = "tcp" },
    { port = 443, protocol = "tcp" }
  ]
}

resource "example_firewall" "app" {
  name = "app"

  ____
}

Which fragment correctly replaces ____?

Options:

  • A.

    dynamic "rule" {
      for_each = var.rules
      iterator = item
      content {
        port     = item.port
        protocol = item.protocol
      }
    }
    
  • B.

    dynamic "rule" {
      for_each = var.rules
      iterator = item
      content {
        port     = item.value.port
        protocol = item.value.protocol
      }
    }
    
  • C.

    dynamic "rules" {
      for_each = var.rules
      iterator = item
      content {
        port     = item.value.port
        protocol = item.value.protocol
      }
    }
    
  • D.

    dynamic "rule" {
      for_each = var.rules
      iterator = item
      content {
        port     = rule.value.port
        protocol = rule.value.protocol
      }
    }
    

Best answer: B

Explanation: A dynamic block generates repeatable nested blocks within a resource; it does not create additional resource instances. Its label must match the provider-defined nested block name, which is rule here. The for_each collection supplies one element per generated block. Because the iterator is explicitly named item, the current element is available as item.value, whose attributes are port and protocol.

The iterator object itself also has key and value attributes, so the input object’s fields cannot be accessed directly from item. Renaming the iterator also means the default iterator name derived from the block label is unavailable.

  • Accessing item.port skips the iterator’s required value attribute.
  • Referencing rule.value fails because the explicit iterator name is item.
  • Using the label rules generates a nested block type that the supplied provider schema does not accept.

Question 29

Topic: Infrastructure Maintenance

An HCP Terraform VCS workspace uses remote execution; provider credentials exist only in the remote environment. Every adoption plan must contain imports and data reads only, with no creates, updates, or destroys.

Existing inventory:

ObjectIDOwnerCurrent facts
Networknet-01Network teamname=shared
Servicesvc-17App teamnetwork=net-01, replicas=4
Policypol-9App teamservice=svc-17, rule=internal
Monitormon-3App teamthreshold=500, independent

The provider-neutral draft is:

data "lab_network" "shared" { id = "net-01" }
resource "lab_service" "app" {
  network_id = data.lab_network.shared.id
  replicas   = 3
}
resource "lab_policy" "internal" {
  service_id = lab_service.app.id
  rule       = "internal"
}
resource "lab_monitor" "latency" {
  threshold_ms = 500
}

Import IDs are the listed object IDs. This provider requires the referenced service to be bound in state at the beginning of a run that imports a policy. The network must remain outside this workspace’s management.

Which adoption approach satisfies all requirements?

Options:

  • A. Set replicas = 4; remotely stage all three managed objects and imports together, relying on the service reference to order policy import.

  • B. Set replicas = 4; run local CLI imports for the service, monitor, and policy sequentially, then review the resulting remote plan.

  • C. Set replicas = 4; import the network as a managed resource first, then remotely import the service, monitor, and policy in later runs.

  • D. Set replicas = 4; remotely stage the service and monitor with their imports, then add and import the policy in a second run.

Best answer: D

Explanation: Configuration-driven import supports reviewed HCP Terraform remote runs, but importing an object does not suppress other planned changes. The service configuration must therefore match the existing replica count before adoption. The service and independent monitor can be imported together. The policy configuration and import must be introduced in a later run because this provider requires the service binding to exist when that run begins. Keeping the network as a data source reads its attributes without transferring management ownership.

A resource reference ordinarily creates a dependency, but it does not override the provider’s stated beginning-of-run import requirement.

  • Single remote run fails because the service is not already bound when the policy import run begins.
  • Local CLI imports fail because terraform import executes locally, where the required provider credentials are unavailable.
  • Managing the network violates its ownership boundary by binding a Network team object into the App team’s state.

Question 30

Topic: Core Workflow

Terraform is applying a plan that destroys all five managed objects shown below. Each arrow points from a dependent to its prerequisite.

Scroll sideways if needed. Open full-size diagram in a new tab

Text description

The API resource depends on the database resource, which depends on the network resource. The audit exporter explicitly depends on the log store. No dependency connects the two branches.

Parallelism allows at least two operations, and no lifecycle rules, provisioners, or other constraints add ordering. Which TWO consequences follow? Select TWO.

Options:

  • A. terraform_data.log_store may be destroyed before terraform_data.audit_export.

  • B. terraform_data.network must be destroyed before terraform_data.log_store.

  • C. terraform_data.audit_export may be destroyed concurrently with terraform_data.api.

  • D. terraform_data.api must be destroyed before terraform_data.database.

  • E. terraform_data.database may be destroyed concurrently with terraform_data.network.

Correct answers: C and D

Explanation: Terraform uses dependency relationships rather than resource labels or file order to schedule operations. During destruction, dependents must be removed before their prerequisites. The first branch therefore follows api, then database, then network. The second branch follows audit_export, then log_store.

No dependency connects the two branches. With sufficient parallelism, ready operations from different branches may overlap, including the initial destruction of api and audit_export. The scheduler is permitted to run independent operations concurrently, but concurrency is not guaranteed.

A dependency imposes ordering within its branch, while the absence of a dependency permits scheduling freedom across branches.

  • Database and network cannot be destroyed concurrently because the database depends on the network and must be removed first.
  • Destroying the log store first would violate the explicit dependency from the audit exporter to that store.
  • Network and log-store destruction have no required cross-branch order, so neither must precede the other.

Question 31

Topic: State Management

A team manages a compute instance using this fictional provider-neutral resource. Changing size updates the existing instance in place.

resource "example_compute_instance" "api" {
  name = "api-1"
  size = "medium"
}

Current facts:

  • Terraform state originally recorded size = "medium".
  • An approved emergency change resized the remote instance to "large".
  • A normal plan now proposes changing it back to "medium".

The business decides to retain "large" permanently, keep Terraform managing size, and avoid an interim downsize. Which approach satisfies these requirements?

Options:

  • A. Update size to "large"; add ignore_changes and apply normally.

  • B. Keep size as "medium"; save and apply a normal plan.

  • C. Update size to "large"; save and apply a normal plan.

  • D. Keep size as "medium"; save and apply a refresh-only plan.

Best answer: C

Explanation: Terraform reconciliation compares configuration intent, state, and the remote object. Because the external resize is now the desired permanent setting, the configuration must be changed to size = "large". A normal plan then refreshes the remote value, confirms that it matches the revised intent, and can persist the reconciled state when applied without resizing the instance.

A refresh-only apply would update state to reflect "large", but it would leave the configuration declaring "medium"; a later normal plan would again propose a downsize. Adding ignore_changes would suppress reconciliation for size, contrary to the requirement that Terraform continue managing it.

  • Refresh state only leaves the configuration at "medium", so the accepted change is not represented as lasting intent.
  • Apply existing intent changes the remote instance back to "medium", violating the business decision and no-downsize constraint.
  • Ignore size drift prevents Terraform from reconciling later size differences instead of continuing full management.

Question 32

Topic: HCP Terraform

A VCS-connected HCP Terraform workspace must run the approved production network configuration from the release branch. The repository contains:

  • projects/network/prod/*.tf
  • projects/network/dev/*.tf
  • projects/app/prod/*.tf
  • modules/network/*.tf

Runs should be triggered only by changes to the production network root or its shared network module. The latest run incorrectly used main, loaded the network development root, and was triggered by an application change.

The following is provider-neutral HCL-like workspace-settings notation. trigger_patterns are repository-root-relative globs, and /** matches all files recursively.

workspace "network-prod" {
  vcs_repository = "acme/infrastructure"
  ____
}

Which fragment should replace ____?

Options:

  • A.

    branch            = "release"
    working_directory = "projects/network/prod"
    trigger_patterns  = ["projects/**", "modules/**"]
    
  • B.

    branch            = "release"
    working_directory = "projects/network/prod"
    trigger_patterns  = ["projects/network/prod/**", "modules/network/**"]
    
  • C.

    branch            = "main"
    working_directory = "projects/network/prod"
    trigger_patterns  = ["projects/network/prod/**", "modules/network/**"]
    
  • D.

    branch            = "release"
    working_directory = "projects/network"
    trigger_patterns  = ["projects/network/prod/**", "modules/network/**"]
    

Best answer: B

Explanation: A VCS-connected HCP Terraform workspace obtains its configuration from the selected branch and executes Terraform from its configured working directory. Terraform does not recursively combine configurations from nested project directories, so projects/network/prod must be the root rather than its parent. Trigger patterns should cover both that root and the shared module whose changes can affect it.

Using broad repository paths would reproduce the unintended application-triggered run. Selecting main would also run an unapproved revision even if the directory and triggers were otherwise correct.

  • Parent network directory fails because nested prod files are not automatically treated as the root module.
  • Main branch selects the integration revision instead of the approved production revision.
  • Broad trigger patterns include unrelated application and module changes, causing unnecessary runs.

Question 33

Topic: Configuration

Terraform 1.12 evaluates this configuration with the input value requested_zones = ["zone-b", "zone-a", "zone-a"]:

variable "requested_zones" {
  type = set(string)
}

locals {
  expected_zones  = tolist(["zone-a", "zone-b"])
  direct_match    = var.requested_zones == local.expected_zones
  normalized_match = var.requested_zones == toset(local.expected_zones)
}

Which TWO outcomes occur? Select TWO.

Options:

  • A. local.normalized_match is false; the original element order differs between its operands.

  • B. local.normalized_match is true; both sets contain the same two strings.

  • C. var.requested_zones has three elements; the set constraint preserves duplicate input values.

  • D. local.direct_match is false; equality does not convert the list to a set.

  • E. local.direct_match is true; equality converts the list to the variable’s set type.

Correct answers: B and D

Explanation: A variable’s type constraint converts its supplied value when a compatible conversion exists. The supplied collection therefore becomes a set(string), and its duplicate "zone-a" is removed. However, Terraform’s equality operator does not automatically convert collections to a common type. Comparing that set directly with expected_zones, which is explicitly a list, produces false even though their unique strings match.

Applying toset explicitly normalizes the list. Set equality compares collection membership rather than declaration or iteration order, so the two normalized sets are equal. The key distinction is that type conversion at an input boundary does not imply conversion during equality comparison.

  • Implicit equality conversion is incorrect because == does not coerce a list into a set before comparing them.
  • Order-dependent set equality is incorrect because set identity depends on membership, not element order.
  • Duplicate retention is incorrect because conversion to a set removes duplicate elements.

Question 34

Topic: Terraform Fundamentals

A CI job must install a private Terraform provider, but terraform init reports that the declared provider cannot be found.

terraform {
  required_providers {
    firewall = {
      source  = ____
      version = "~> 2.3"
    }
  }
}

Registry evidence:

  • DNS and TLS connections to registry.corp.example succeed.
  • CLI credentials provide an accepted token for that hostname.
  • The authenticated catalog lists registry.corp.example/netops/firewall version 2.3.4.

Which value should replace ____ so initialization resolves the intended provider?

Options:

  • A. "registry.corp.example/netops/firewall-provider"

  • B. "registry.terraform.io/netops/firewall"

  • C. "registry.corp.example/platform/firewall"

  • D. "registry.corp.example/netops/firewall"

Best answer: D

Explanation: Terraform identifies a provider package by its complete source address: hostname, namespace, and provider type. The local name firewall does not determine that identity. Here, connectivity and authentication to the private registry are working, while the catalog identifies the package specifically as registry.corp.example/netops/firewall. The version constraint also permits the listed 2.3.4 release.

Changing any source-address component makes Terraform request a different provider package. Valid registry credentials authorize access but do not redirect an incorrect namespace, type, or hostname to the intended package.

  • Wrong namespace: platform identifies a different provider package than the cataloged netops namespace.
  • Wrong provider type: firewall-provider does not match the cataloged provider type firewall.
  • Wrong registry host: registry.terraform.io directs initialization to the public registry instead of the authenticated private registry.

Question 35

Topic: Modules

A team combines two independently maintained private registry modules. It must retain both modules, continue using AWS provider 5.x, preserve existing state bindings, and review changes before applying.

Root configuration:

terraform {
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.40"
    }
  }
}

module "network" {
  source  = "app.terraform.io/acme/network/aws"
  version = "2.3.0"
}

module "service" {
  source  = "app.terraform.io/acme/service/aws"
  version = "4.1.0"
}

Initialization diagnostic:

module.network requires: ~> 4.60
module.service requires: >= 5.20, < 6.0
Locked provider selection: 5.44.0
Error: no available releases match all constraints

An approved network module version 3.0.0 requires >= 5.20, < 6.0. Its interface and resource addresses are unchanged.

Which approach resolves the conflict sustainably?

Options:

  • A. Set module.network to 3.0.0, run terraform init -upgrade, commit the configuration and lock file, then review a normal plan.

  • B. Broaden the root constraint to >= 4.60, < 6.0, delete the lock file, rerun initialization, then review a normal plan.

  • C. Change the locked selection to 4.67.0, pin the root requirement to that version, rerun initialization, then review a normal plan.

  • D. Define aws.legacy and aws.current aliases, pass one to each child module, rerun initialization, then review a normal plan.

Best answer: A

Explanation: Terraform combines all version requirements for the same provider source and installs one provider version for the configuration. The original child requirements do not overlap: ~> 4.60 excludes 5.x, while the service module requires at least 5.20. Updating the network module changes its requirement so that all three constraints intersect at >= 5.40, < 6.0.

Running terraform init -upgrade installs the approved module release and selects an allowed provider version. The updated dependency configuration and .terraform.lock.hcl should be committed before reviewing the plan. The lock file records a selected provider version and checksums; it cannot override incompatible configuration requirements.

  • Broaden the root constraint fails because the two child-module requirements still have no common provider version.
  • Use provider aliases fails because aliases create multiple configurations of one installed provider version, not multiple versions of the same provider.
  • Select provider 4.67.0 fails because that version violates the service module’s requirement, and the lock file cannot override it.

Question 36

Topic: Core Workflow

A team created and reviewed a saved plan from repository commit A:

terraform plan -out=release.tfplan

The plan used remote backend state serial 42. Before apply:

  • Commit B changed terraform_data.release.input to the required release value.
  • An unrelated authorized apply successfully updated infrastructure and wrote state serial 43.
  • The current checkout is commit B, and no further runs will occur during this procedure.

The team has one apply window and must deploy commit B while retaining the changes recorded in serial 43. The exact operations applied must be reviewed. Which approach should the team use?

Options:

  • A. Review an unsaved commit B plan, then apply the already reviewed commit A saved plan.

  • B. Replace the old artifact with a commit B plan, review it, and apply that saved plan.

  • C. Perform a refresh-only apply, review the commit A plan again, then apply that saved plan.

  • D. Replan commit A against serial 43, review and apply it, then schedule commit B later.

Best answer: B

Explanation: A saved plan is immutable and tied to the configuration and state snapshot used to create it. Pulling commit B does not update release.tfplan. Because the authoritative state advanced from serial 42 to 43, Terraform will also treat the old artifact as stale. Even without that state change, the artifact would still contain commit A’s proposed operations rather than commit B’s configuration.

The team must create a new saved plan from commit B against the current backend state, review that specific artifact, and apply it. This preserves the intervening state changes and ensures the reviewed operations match the required release.

  • Reviewing an unsaved commit B plan does not modify the old artifact, which remains based on commit A and stale state.
  • A refresh-only apply updates state rather than regenerating the saved plan, so the old artifact remains unusable.
  • Replanning commit A can use current state, but it does not deploy commit B within the authorized window.

Question 37

Topic: Configuration

A team uses terraform_data to model deployment metadata. The network metadata must record the predetermined service name, while the service must record the network ID generated during apply.

Which expression should replace ____ so Terraform can build an acyclic dependency graph?

terraform {
  required_version = "~> 1.12.0"
}

variable "environment" {
  type    = string
  default = "prod"
}

locals {
  network_name = "${var.environment}-network"
  service_name = "${var.environment}-api"
}

resource "terraform_data" "network" {
  input = {
    name         = local.network_name
    service_name = ____
  }
}

resource "terraform_data" "service" {
  input = {
    name       = local.service_name
    network_id = terraform_data.network.id
  }
}

output "inventory" {
  value = {
    network = terraform_data.network.output.name
    service = terraform_data.service.output.name
  }
}

Options:

  • A. local.service_name

  • B. coalesce(terraform_data.service.output.name, local.service_name)

  • C. terraform_data.service.input.name

  • D. try(terraform_data.service.output.name, local.service_name)

Best answer: A

Explanation: Terraform derives graph edges from configuration references. The service already depends on the network because its input references terraform_data.network.id. If the network also references any attribute of the service, the two resources form a cycle. The service name is independently known from local.service_name, so the network can record that value without depending on the service resource. The inventory output may reference both resources because it consumes their values without becoming a prerequisite for either resource.

Functions such as try and coalesce do not remove dependency edges created by resource references. The key is to source predetermined data independently while retaining the genuine service-to-network dependency.

  • Referencing the service input still creates a network-to-service dependency, completing the cycle.
  • Using try retains the service reference, and an unknown value is not a dynamic evaluation error that triggers the fallback.
  • Using coalesce retains the service reference and therefore does not remove the graph edge.

Question 38

Topic: Infrastructure as Code

A team manages an API worker pool with the Terraform CLI. The provider creates one remote worker for each resource instance.

resource "acme_worker" "api" {
  count = 2
  pool  = "api"
}

State contains acme_worker.api[0] and [1]. A saved baseline.tfplan, created before a new request, reports no changes. The team now requires three workers. The exact execution plan must be reviewed before the maintenance window, then applied during it. No automation runs Terraform when files change.

Which approach satisfies the requirement?

Options:

  • A. Keep count = 2; provision a third worker directly; use refresh-only apply to adopt it.

  • B. Commit count = 3; save and review a new plan; apply that saved plan.

  • C. Commit count = 3; review an unsaved plan; run a separate apply during the window.

  • D. Commit count = 3; validate the configuration; wait for Terraform to reconcile the repository.

Best answer: B

Explanation: Terraform configuration declares desired state, but the Terraform CLI is not a continuously running controller. Changing count from 2 to 3 causes a new plan to propose acme_worker.api[2]. Saving that plan with terraform plan -out=scale.tfplan, reviewing it with terraform show scale.tfplan, and later applying the same file preserves the required approval boundary.

The earlier saved plan contains the old no-change proposal. An unsaved plan cannot be applied later as the reviewed artifact because a subsequent terraform apply creates a new plan. Refresh-only operations update state for existing bindings; they neither adopt unmanaged objects nor rewrite configuration. A Terraform run is required to enact declarative intent.

  • Unsaved plan fails because the later apply generates a different execution plan that was not reviewed before the window.
  • Refresh-only adoption fails because refresh-only does not bind an unmanaged worker, and the configuration still declares two instances.
  • Passive reconciliation fails because committing and validating HCL do not cause the Terraform CLI to provision infrastructure.

Question 39

Topic: State Management

A repository currently contains this managed resource:

resource "aws_s3_bucket" "audit_archive" {
  bucket = "example-audit-archive-4821"

  lifecycle {
    prevent_destroy = true
  }
}

State tracks aws_s3_bucket.audit_archive. Another team will manage the existing bucket, which must remain available. A successful provider destroy would delete it. The handoff intent must be reviewable in Git and executed through a normal plan and apply.

The engineer removes the resource declaration:

# main.tf after handoff
____

Which focused edit completes the handoff?

Options:

  • A. Add a removed block for the address with destroy = false.

  • B. Add a removed block for the address with destroy = true.

  • C. Leave the file blank and run terraform state rm for the address.

  • D. Rename the resource and add a moved block from the old address.

Best answer: A

Explanation: A removed block declares that Terraform should relinquish an existing state binding. Setting lifecycle { destroy = false } causes the subsequent plan to show that Terraform will forget the object rather than destroy it. Applying that plan removes the binding from state while leaving the remote bucket available, and the handoff intent remains recorded in version-controlled configuration.

The terraform state rm command can also forget a binding without destroying the object, but it is an imperative state operation rather than the required configuration-driven, reviewable workflow. A moved block preserves management under another Terraform address instead of relinquishing it.

  • State removal command preserves the bucket but does not record the preservation intent as a configuration-driven plan and apply.
  • Destroy enabled directs Terraform to delete the remote bucket instead of merely forgetting its binding.
  • Moved address transfers the binding to another Terraform address, so Terraform still manages the bucket.

Question 40

Topic: HCP Terraform

An HCP Terraform Sentinel policy set is scoped to the Payments project, covering all its workspaces but not the Sandbox project. A policy uses soft-mandatory enforcement and has failed for the current payments-api run.

The exception is approved for this run only. The developer can apply runs but cannot override policy checks; a governance reviewer can override them. Future Payments runs must retain the same policy scope and enforcement.

Which approach satisfies these requirements?

Options:

  • A. Have the reviewer override this run, then have the developer approve its apply

  • B. Detach the policy set from Payments, apply the run, then reattach the set

  • C. Change enforcement to advisory, apply the run, then restore soft-mandatory enforcement

  • D. Scope the policy set to payments-api, queue another run, then approve its apply

Best answer: A

Explanation: A soft-mandatory Sentinel failure blocks an HCP Terraform run unless a user with policy-override permission grants an exception. That override applies to the particular run; it does not change the policy set’s project scope or enforcement level. Apply permission is separate, so the developer can approve the apply only after the governance reviewer overrides the failed check.

Changing enforcement, detaching the policy set, or narrowing its scope would alter governance for other runs rather than granting the required one-time exception.

  • Advisory enforcement would make failures nonblocking for other affected runs while the policy remains advisory.
  • Policy detachment would temporarily remove the required checks from every workspace in the Payments project.
  • Workspace-only scope would stop the policy set from governing the other Payments workspaces.

Question 41

Topic: Configuration

A team manages a provider-neutral fleet resource. The provider requires both arguments at creation and can update either in place.

resource "example_fleet" "api" {
  desired_capacity = 3
  image_revision   = "v4"
}

An external autoscaler legitimately controls only desired_capacity. Terraform owns image_revision.

State records capacity 3 and revision v4, while the remote object now has capacity 7 and revision v5. The next plan must preserve capacity 7 but restore revision v4.

Which approach satisfies these requirements?

Options:

  • A. Keep both configured values and add ignore_changes = [desired_capacity, image_revision].

  • B. Keep both configured values and add ignore_changes = [desired_capacity].

  • C. Keep both configured values and add ignore_changes = all.

  • D. Keep both configured values and add ignore_changes = [image_revision].

Best answer: B

Explanation: The ignore_changes lifecycle rule lets Terraform share management of selected resource attributes. Terraform still uses desired_capacity = 3 when creating a new fleet, but it disregards later differences in that attribute when planning updates. Therefore, the autoscaler’s remote capacity of 7 remains unchanged.

Only desired_capacity belongs to the external controller. Because image_revision is not ignored, Terraform detects its drift from configured revision v4 to remote revision v5 and plans an in-place update back to v4. The resource remains in Terraform state and continues to be managed for all other attributes.

  • Ignoring both attributes would also suppress the required correction from image revision v5 to v4.
  • Ignoring only the image revision would tolerate unauthorized image drift and plan to return capacity to 3.
  • Ignoring all attributes would prevent Terraform from planning updates for any future resource drift.

Question 42

Topic: Core Workflow

A CI job must classify a Terraform plan and retain the appropriate review evidence:

  • No changes: route no_changes, succeed.
  • Changes proposed: upload tfplan, route changes, succeed.
  • Planning error: upload plan.log, route error, fail.

upload <file> retains a file. finish <route> <result> records the route and ends the branch. set +e permits status capture.

set +e
terraform plan -detailed-exitcode -out=tfplan >plan.log 2>&1
rc=$?
set -e
____

Which fragment should replace ____?

Options:

  • A. case "$rc" in 2) finish no_changes success;; 0) upload tfplan; finish changes success;; 1) upload plan.log; finish error failure;; esac

  • B. case "$rc" in 0) finish no_changes success;; 2) upload tfplan; finish changes failure;; 1) upload plan.log; finish error failure;; esac

  • C. case "$rc" in 0) finish no_changes success;; 1) upload tfplan; finish changes success;; 2) upload plan.log; finish error failure;; esac

  • D. case "$rc" in 0) finish no_changes success;; 2) upload tfplan; finish changes success;; 1) upload plan.log; finish error failure;; esac

Best answer: D

Explanation: With -detailed-exitcode, Terraform assigns three meanings: 0 indicates a successful plan with no changes, 2 indicates a successful plan that proposes changes, and 1 indicates an error. Exit code 2 is therefore not a failed plan despite being nonzero. Capturing $? immediately preserves the Terraform result for routing. A changes result can publish the saved plan for review and complete successfully. An error should publish the captured diagnostic output and fail the job.

The key distinction is that code 2 reports a detected difference, not a command failure.

  • Treating code 1 as proposed changes misclassifies a planning error and may attempt to publish an invalid plan artifact.
  • Routing code 0 as changes reverses the meanings of successful no-change and successful change results.
  • Failing the job for code 2 applies ordinary nonzero-exit handling instead of Terraform’s documented detailed-exit-code semantics.

Question 43

Topic: Infrastructure Maintenance

A Linux CI runner executes Terraform CLI locally. init and plan succeed, but apply intermittently fails during a provider operation.

Requirements:

  • Capture TRACE-level Terraform core and provider diagnostics only during apply.
  • Write the new log to the approved $RUNNER_TEMP/secure/terraform-debug.log path.
  • Allow only the file owner to read or write the log.
  • Leave diagnostic logging disabled for subsequent commands.

The logging variables are initially unset, and Bash automatic exporting (set -a) is disabled. TF_LOG_PATH selects a log file but requires logging to be enabled. Exported variables reach child processes. Parentheses run commands in a subshell; braces use the current shell.

terraform init
terraform plan -out=tfplan
____
terraform state list

Which fragment should replace ____?

Options:

  • A.

    (
      umask 077
      export TF_LOG_PROVIDER=TRACE
      export TF_LOG_PATH="$RUNNER_TEMP/secure/terraform-debug.log"
      terraform apply tfplan
    )
    
  • B.

    {
      umask 077
      export TF_LOG=TRACE
      export TF_LOG_PATH="$RUNNER_TEMP/secure/terraform-debug.log"
      terraform apply tfplan
    }
    
  • C.

    (
      umask 077
      TF_LOG=TRACE
      TF_LOG_PATH="$RUNNER_TEMP/secure/terraform-debug.log"
      terraform apply tfplan
    )
    
  • D.

    (
      umask 077
      export TF_LOG=TRACE
      export TF_LOG_PATH="$RUNNER_TEMP/secure/terraform-debug.log"
      terraform apply tfplan
    )
    

Best answer: D

Explanation: Terraform diagnostic logging is controlled through environment variables visible to the Terraform process. TF_LOG=TRACE enables detailed core and provider logging, while TF_LOG_PATH sends it to the approved file. Running the exports and apply inside a parenthesized subshell prevents those settings from affecting later Terraform commands. Because the log does not already exist, umask 077 restricts its creation permissions to the owner.

Assignments that are not exported do not reach the Terraform child process. Provider-only logging also omits Terraform core evidence that may be needed to diagnose the operation.

  • Unexported variables remain shell-local, so the Terraform process does not receive the diagnostic settings.
  • Provider-only logging omits Terraform core diagnostics required by the collection plan.
  • Current-shell grouping leaves the exported logging variables active for the subsequent terraform state list command.

Question 44

Topic: Terraform Fundamentals

A team commits one .terraform.lock.hcl file. The configuration requires hashicorp/random version ~> 3.7.0, and the lock file currently selects version 3.7.2.

Lock-file review:

Provider packageChecksum recorded
linux_amd64Yes
darwin_arm64No

The lock file was created through a Linux-only filesystem mirror. The origin registry publishes version 3.7.2 and signed checksums for both platforms. Version 3.7.3 is also available in this scenario. The team must verify the added package checksums against the origin registry’s signed checksum data, retain version 3.7.2 and commit one lock file that supports normal initialization on both platforms.

Which approach satisfies these requirements?

Options:

  • A. Calculate the macOS package checksum manually, append it to the lock file, and commit the change.

  • B. Run terraform init -upgrade on both platforms, review and commit the last lock file produced.

  • C. Run terraform providers lock for both platforms, review and commit the updated lock file.

  • D. Maintain separate committed lock files per platform and copy the applicable file before initialization.

Best answer: C

Explanation: The dependency lock file records selected provider versions and acceptable package checksums. Running terraform providers lock -platform=linux_amd64 -platform=darwin_arm64 obtains checksum information for both target packages from the origin registry. The compatible existing lock selection keeps the operation on version 3.7.2; this command adds checksum coverage rather than requesting a version upgrade. After the updated file is reviewed and committed, normal initialization on either platform can verify its downloaded provider package against the shared lock file.

Using platform-specific lock files defeats the goal of a single reproducible dependency selection.

  • Upgrade during repair permits Terraform to select a newer version allowed by ~> 3.7.0, contrary to retaining 3.7.2.
  • Manual checksum editing bypasses Terraform’s supported process for retrieving and verifying the registry’s signed checksum data.
  • Separate lock files fragment dependency selections instead of providing one committed lock file for the team.

Question 45

Topic: Configuration

A root module currently manages components with positional identities:

resource "terraform_data" "component" {
  count = length(var.components)
  input = var.components[count.index]
}

Current state:

  • [0] = api
  • [1] = worker
  • [2] = web

The caller changes the list to web, api, jobs. Component IDs are unique and immutable, but list order may change again. The plan must preserve the existing api and web bindings, remove worker, and create jobs.

Which approach satisfies these requirements?

Options:

  • A. Key for_each by ID; move [0] to web, [1] to api, and [2] to jobs.

  • B. Retain count; sort the input by ID before applying and continue using the existing indexed addresses.

  • C. Key for_each by ID; move [0] to api and [2] to web, leaving the other instances unmapped.

  • D. Key for_each by string index; move each numeric address to its equivalent string-keyed address.

Best answer: C

Explanation: count identifies instances by numeric position, so collection reordering can associate existing addresses with different components. A for_each map keyed by immutable component ID gives each component a stable address independent of list order. During migration, moved blocks must follow existing identities: [0] becomes ["api"], and [2] becomes ["web"]. The unmoved [1] binding for worker is destroyed, while the new ["jobs"] instance is created.

Moving instances according to the new positions would preserve addresses but attach them to the wrong component identities.

  • New-order mapping incorrectly reassigns existing bindings according to positions rather than their current component identities.
  • String indexes remain order-dependent even though their type changes from numbers to strings.
  • Sorting with count can shift existing indexed bindings and does not provide durable identity when membership changes.

Question 46

Topic: Modules

A team uses one root module for multiple environments. Terraform runs from the root directory with the CLI workspace default selected.

Root configuration:

variable "environment_name" {
  type    = string
  default = "development"
}

module "app" {
  source = "./modules/app"
  ____
}

Root input:

# prod.auto.tfvars
environment_name = "production"

Child module:

variable "environment_name" {
  type    = string
  default = "development"
}

resource "terraform_data" "settings" {
  input = {
    environment = var.environment_name
  }
}

Which fragment should replace ____ so the child records the environment supplied through the root input?

Options:

  • A. environment_name = "var.environment_name"

  • B. environment_name = null

  • C. environment_name = terraform.workspace

  • D. environment_name = var.environment_name

Best answer: D

Explanation: A child module receives values only through arguments in its module call. The root loads prod.auto.tfvars, assigning production to the root variable. The module argument must then reference that root variable and assign its value to the child’s environment_name input. The child resource consequently records production.

Root variable files are not implicitly loaded into child modules. If the argument were omitted, the child would independently use its development default. The module boundary therefore requires explicit value flow from the caller’s variable to the child’s declared variable.

  • Using terraform.workspace supplies default, not the value loaded from prod.auto.tfvars.
  • Quoting the variable reference passes the literal text var.environment_name rather than evaluating it.
  • Passing null explicitly overrides the nullable child variable’s default instead of forwarding production.

Question 47

Topic: Core Workflow

A service currently has exactly two healthy API instances behind a load balancer. The team must deploy image v2, replace the legacy monitor, and keep at least two healthy instances registered throughout the apply. Temporary additional instances are allowed.

The provider-neutral resources have these semantics:

  • Changing image requires replacement.
  • Instance creation completes only after the instance is healthy.
  • Load balancer membership updates are atomic.
  • Monitor changes do not affect request routing.

The reviewed saved plan was generated after changing image from v1 to v2:

resource "example_instance" "api" {
  for_each = toset(["a", "b"])
  image    = "v2"

  lifecycle {
    create_before_destroy = true
  }
}

resource "example_load_balancer" "api" {
  members = [for instance in example_instance.api : instance.id]
}
+/- example_instance.api["a"]  image: "v1" -> "v2"
+/- example_instance.api["b"]  image: "v1" -> "v2"
  ~ example_load_balancer.api  members: old IDs -> known after apply
  + example_monitor.latency
  - example_monitor.legacy

Which approach satisfies the deployment and availability requirements?

Options:

  • A. Apply the reviewed saved plan, then confirm convergence with a normal plan.

  • B. Disable create-before-destroy, regenerate and apply, then confirm convergence with a normal plan.

  • C. Ignore image changes, regenerate and apply, then confirm convergence with a normal plan.

  • D. Pin old member IDs, apply replacements, then switch IDs and run another apply.

Best answer: A

Explanation: The action markers show two create-before-destroy replacements (+/-), one in-place update (~), one creation (+), and one deletion (-). Each replacement becomes healthy before creation completes. Because the load balancer references the instance IDs, Terraform updates its membership before destroying the old instances. The atomic membership update therefore switches from two healthy old instances to two healthy replacements without dropping below the required capacity. Temporary coexistence is permitted, so creating additional instances is acceptable. The independent monitor creation and deletion also match the intended change.

A normal plan after applying the reviewed saved plan confirms that the complete configuration has converged.

  • Disabling create-before-destroy permits old instances to be removed before their replacements are healthy, reducing capacity below two.
  • Ignoring the image attribute prevents Terraform from performing the required v2 rollout.
  • Pinning old IDs removes the dependency-driven membership transition and can leave the load balancer referencing destroyed instances between applies.

Question 48

Topic: State Management

A team uses Terraform CLI with a remote backend that supports state locking. A canceled apply left this lock:

Lock ID: 7ad4c2ef
Operation: OperationTypeApply
Who: ci-runner

The CI system shows that the job ended and its runner was terminated. The run owner confirms no Terraform process remains, and no other runs are active. An authorized operator must clear the lock from the initialized working directory and reevaluate current configuration and state before making changes.

Which complete approach should the operator take?

Options:

  • A. Run terraform init -reconfigure, confirm backend access, then create and review a fresh plan.

  • B. Run terraform plan -lock-timeout=10m, wait for lock expiry, then review that fresh plan.

  • C. Run terraform force-unlock 7ad4c2ef, confirm, then apply the canceled job’s saved plan.

  • D. Run terraform force-unlock 7ad4c2ef, confirm, then create and review a fresh plan.

Best answer: D

Explanation: terraform force-unlock manually removes a state lock using its unique lock ID. It is appropriate only after verifying that the operation holding the lock has ended and that no legitimate run is still using the state. Those conditions are met here, and the operator is authorized to act. Force-unlocking does not modify infrastructure or reconcile state; it only clears the lock. A fresh plan is therefore needed to evaluate the current configuration, state, and remote objects before another apply. Never force-unlock merely because another valid operation is taking longer than expected.

  • Backend reconfiguration updates backend initialization settings but does not remove an existing stale state lock.
  • Lock timeout controls how long Terraform waits to acquire a lock; it does not expire or clear an orphaned lock.
  • Reusing the saved plan skips the required reevaluation after the canceled operation and may rely on stale run context.

Question 49

Topic: HCP Terraform

Maya can view an HCP Terraform run for payments-prod, but Confirm & Apply is unavailable. She belongs only to Reviewers. An organization owner can add her to one team.

TeamScopeAccess
ReviewersPayments projectRead
Prod Plannerspayments-prodPlan
Prod Operatorspayments-prodWrite
Project MaintainersPayments projectWrite

The access policy states that Write access is required to confirm an apply. The current run is valid, has passed policy checks, and awaits confirmation. No manual lock or competing run exists, and remote provider credentials are ready. Maya must not receive apply access to other Payments workspaces.

Which approach satisfies the requirement?

Options:

  • A. Add Maya to Prod Operators, then confirm the existing run.

  • B. Unlock payments-prod, then retry confirmation of the existing run.

  • C. Add Maya to Prod Planners, then queue and confirm a replacement run.

  • D. Add Maya to Project Maintainers, then confirm the existing run.

Best answer: A

Explanation: HCP Terraform team access determines which run actions a user can perform. Read access explains why Maya can view the run, but confirming an apply requires Write access. Membership in Prod Operators provides that capability at only the required workspace, preserving the stated project boundary.

The existing run does not need to be replaced because its plan and policy results remain valid. Workspace locks control run serialization, while provider credentials authorize Terraform against the target platform; neither grants Maya permission to confirm an HCP Terraform apply.

  • Project Maintainers provides Write access but improperly extends it to every workspace in the Payments project.
  • Prod Planners permits planning, not apply confirmation, so creating another run does not resolve the authorization failure.
  • Unlocking addresses run blocking, but no lock exists and unlocking does not grant Write access.

Question 50

Topic: Configuration

A local module input named policy_json requires a JSON string that decodes to these types:

FieldRequired JSON type
enabledboolean
retriesnumber
labelsarray of strings
messagestring

Complete the caller configuration so all values retain their types and special characters are escaped correctly.

variable "enabled" {
  type    = bool
  default = true
}

variable "retries" {
  type    = number
  default = 3
}

variable "labels" {
  type    = list(string)
  default = ["blue", "api"]
}

variable "message" {
  type    = string
  default = "Deploy \"blue\"\nnow"
}

locals {
  policy_json = ____
}

module "policy_uploader" {
  source      = "./modules/policy-uploader"
  policy_json = local.policy_json
}

Options:

  • A.

    jsonencode({
      enabled = var.enabled
      retries = var.retries
      labels  = jsonencode(var.labels)
      message = jsonencode(var.message)
    })
    
  • B.

    <<-JSON
    {
      "enabled": ${var.enabled},
      "retries": ${var.retries},
      "labels": ${jsonencode(var.labels)},
      "message": "${var.message}"
    }
    JSON
    
  • C.

    jsonencode({
      enabled = tostring(var.enabled)
      retries = tostring(var.retries)
      labels  = join(",", var.labels)
      message = var.message
    })
    
  • D.

    jsonencode({
      enabled = var.enabled
      retries = var.retries
      labels  = var.labels
      message = var.message
    })
    

Best answer: D

Explanation: Terraform’s jsonencode function converts a typed Terraform value into valid JSON recursively. Booleans become JSON booleans, numbers remain numbers, and lists become arrays. String characters that have special meaning in JSON, including quotation marks and newlines, are escaped automatically. The entire object should therefore be constructed as a typed Terraform value and encoded once at its outer boundary.

Manual string construction shifts responsibility for escaping to the configuration author. Encoding individual nested values before encoding the containing object instead produces strings containing serialized JSON rather than the required nested values.

  • Convert values first changes the Boolean and number into strings and collapses the labels into one string.
  • Build JSON manually inserts the message into a quoted JSON field without safely escaping its quotation marks and newline.
  • Encode nested values makes the labels and message contain serialized JSON text instead of the required array and original string.

Questions 51-60

Question 51

Topic: Infrastructure as Code

A team uses the local Terraform CLI for a controlled release:

  • A pull request containing the Terraform configuration and .terraform.lock.hcl is approved and merged.
  • CI checks out the exact commit and creates release.tfplan with terraform plan -out=release.tfplan.
  • A reviewer inspects the saved plan with terraform show.
  • The same controlled runner will apply the plan; no configuration, variable, or state changes occur first.

No infrastructure writes have occurred yet. Which TWO statements accurately describe this process? Select TWO.

Options:

  • A. The saved plan preserves the reviewed proposal for direct application.

  • B. The commit and lock file confirm remote objects still match state.

  • C. The commit and lock file identify reviewed configuration and provider selections.

  • D. The saved plan recalculates changes from the latest configuration during apply.

  • E. The successful plan confirms the apply identity has all write permissions.

Correct answers: A and C

Explanation: Version control and the dependency lock file provide traceability for the reviewed configuration and selected provider packages. The saved plan records Terraform’s proposed actions based on the configuration, variables, and state available during planning. Reviewing that artifact provides change visibility, and applying its filename avoids generating a new plan.

These controls do not perform infrastructure changes or prove that every planned API operation will succeed. Planning may require read access, but it does not establish permission for every create, update, or delete operation. Apply can still fail because of authorization, remote conditions, or provider errors. The key distinction is between making a proposed change reviewable and proving its eventual execution.

  • Remote object agreement is not established by Git or the provider lock file; detecting drift requires Terraform to inspect remote infrastructure.
  • Write authorization is not proven by successful planning because planned mutations have not yet been attempted.
  • Automatic recalculation does not occur when applying a saved plan; changed inputs or configuration require creating and reviewing a fresh plan.

Question 52

Topic: Core Workflow

A contributor has a clean checkout with no .terraform directory. The configuration declares a production S3 backend and references a version-constrained registry module and provider. The provider lock file is committed.

The contributor has no backend credentials, and production backend requests are prohibited. Approved registry downloads are permitted. The team requires recursive formatting checks and internal configuration validation, but no plan.

Which command sequence satisfies these requirements?

Options:

  • A. Run terraform init -backend=false, then terraform fmt -check -recursive and terraform validate.

  • B. Run terraform init, then terraform fmt -check -recursive and terraform validate.

  • C. Run terraform init -backend=false -get=false, then terraform fmt -check -recursive and terraform validate.

  • D. Run terraform init -backend=false, then terraform fmt -check -recursive and terraform plan -refresh=false.

Best answer: A

Explanation: terraform init -backend=false installs required modules and providers while skipping initialization of the configured production backend. This prepares a clean checkout for terraform validate, which checks syntax and internal consistency using installed provider schemas and modules. terraform fmt -check -recursive verifies canonical formatting without changing files.

These checks do not prove that backend credentials work, remote APIs are reachable, capacity exists, or the proposed infrastructure changes are acceptable. A plan requires additional execution context and is unnecessary for the stated local checks. The key distinction is that disabling backend initialization does not disable dependency installation.

  • Normal initialization attempts to initialize the configured production backend, violating the prohibition on backend requests.
  • Disabled module retrieval leaves the registry module unavailable in a clean checkout, preventing complete validation.
  • Refresh-disabled planning does not replace validation or disable backend requirements; it only suppresses refreshing managed objects during planning.

Question 53

Topic: Infrastructure Maintenance

Terraform state was last updated by a successful apply at 09:00. At 11:00, an administrator changed a managed object’s label from blue to green outside Terraform. No Terraform operation has refreshed state since 09:00.

At 12:00, the live API reports green, while terraform state show and terraform output report blue. The team wants to investigate without changing infrastructure or persisting refreshed state. Which TWO statements are accurate?

Options:

  • A. A refresh-only plan can preview current remote values without changing infrastructure or persisting state.

  • B. A plan with -refresh=false detects the external change while avoiding modifications to state.

  • C. Both inspection commands report the 09:00 snapshot without automatically querying the provider.

  • D. The output command queries the live API but preserves the value recorded during apply.

  • E. The state-show command refreshes the object but delays saving the updated value until apply.

Correct answers: A and C

Explanation: Terraform state is a persisted snapshot, not a continuously synchronized view of infrastructure. Because the snapshot was last updated at 09:00, both terraform state show and terraform output can still report blue after the 11:00 external change. Neither command automatically performs a fresh provider read.

A refresh-only plan queries managed objects and previews how Terraform would update state and outputs to reflect the live value. Planning does not change the remote object or persist the refreshed snapshot. If the team later accepts the external change, it can review a refresh-only apply and ensure configuration remains consistent with the intended value.

  • Live output query is incorrect because terraform output reads output values from state rather than querying the remote API.
  • Implicit state refresh is incorrect because terraform state show displays the stored resource instance without refreshing it.
  • Refresh disabled cannot detect the live change because -refresh=false prevents the provider reads needed to discover drift.

Question 54

Topic: Configuration

A developer runs Terraform CLI locally. CI must load approved.tfvars and produce a plan with owner = "release-team" and deployment_color = "green".

# variables.tf
variable "deployment_color" { default = "gray" }
variable "owner" { default = "platform" }

# terraform.tfvars
deployment_color = "red"

# 20-team.auto.tfvars
deployment_color = "yellow"
owner            = "team-auto"

# approved.tfvars
deployment_color = "blue"
owner            = "release-team"

The environment contains TF_VAR_deployment_color=orange.

Which fragment should replace ____?

terraform plan ____ -out=tfplan

Options:

  • A. -var-file=20-team.auto.tfvars -var='deployment_color=green'

  • B. -var='owner=release-team' -var='deployment_color=green'

  • C. -var-file=approved.tfvars -var='deployment_color=green'

  • D. -var='deployment_color=green' -var-file=approved.tfvars

Best answer: C

Explanation: For a local CLI run, Terraform resolves root variable values from lower- to higher-precedence sources: defaults, environment variables, terraform.tfvars, automatically loaded *.auto.tfvars files, and explicit command-line assignments. Multiple -var and -var-file arguments are processed in command-line order, so the last assignment for a variable wins.

Loading approved.tfvars sets both requested values initially. Placing the green -var assignment afterward changes only deployment_color, leaving the approved owner intact. Reversing those arguments allows the file’s blue value to overwrite green.

  • Placing the variable assignment first allows the later approved file to restore the color to blue.
  • Explicitly loading the team file leaves the owner as team-auto and omits the required approved file.
  • Supplying both current values directly produces them now but violates the requirement to load the approved file.

Question 55

Topic: Terraform Fundamentals

A team copies this configuration from a sandbox working directory into a production working directory:

resource "aws_s3_bucket" "logs" {
  bucket = var.bucket_name
  tags = {
    ManagedBy = "Terraform"
  }
}

Production facts:

  • var.bucket_name is acme-prod-logs.
  • The production backend has empty state.
  • The existing production bucket must be adopted, not recreated.
  • The sandbox state must remain unchanged and binds a different bucket.
  • The provider imports a bucket using its exact name as the import ID.

Configuration changes may be applied only after plan review. Which approach satisfies the task?

Options:

  • A. Initialize, run a normal apply, import after any creation conflict, then review a new plan.

  • B. Initialize, import the production bucket by name, review a normal plan, then apply approved changes.

  • C. Initialize, run a refresh-only apply, review a normal plan, then apply approved changes.

  • D. Initialize, push the sandbox state snapshot, refresh its bindings, then review a normal plan.

Best answer: B

Explanation: Terraform state binds a resource address to a specific remote object identity. Copying HCL into a working directory with empty state does not make Terraform discover or assume ownership of matching infrastructure. After initialization, terraform import aws_s3_bucket.logs acme-prod-logs establishes the intended production binding. A normal plan can then compare the imported object’s attributes with the copied configuration, allowing the team to review any proposed tag or other changes before applying them.

A refresh-only operation cannot discover an object that has no state binding, while applying before import treats the configured bucket as a new managed object.

  • Refresh-only apply updates existing bindings but cannot add the unbound production bucket to empty state.
  • Apply before import proposes creation because Terraform does not yet associate the address with the existing bucket.
  • Push sandbox state transfers bindings for different infrastructure rather than adopting the production bucket; it can make the production root act on sandbox objects.

Question 56

Topic: Modules

A root-managed resource is being moved into a child module. State currently binds terraform_data.app to object ID 2c0f3fe1-3c90-4487-a620-80f17f83b254 with input v1.

Previous root configuration:

resource "terraform_data" "app" {
  input = "v1"
}

New root configuration:

module "service" {
  source  = "./modules/service"
  payload = "v1"
}

____

Child module configuration:

variable "payload" {
  type = string
}

resource "terraform_data" "app" {
  input = var.payload
}

Which fragment should replace ____ so a reviewed plan reports the address move without creating or destroying the object?

Options:

  • A.

    moved {
      from = module.service.terraform_data.app
      to   = terraform_data.app
    }
    
  • B.

    moved {
      from = "terraform_data.app"
      to   = "module.service.terraform_data.app"
    }
    
  • C.

    moved {
      from = terraform_data.app
      to   = module.service.terraform_data.app
    }
    
  • D.

    moved {
      from = terraform_data.app
      to   = module.service.terraform_data.app[0]
    }
    

Best answer: C

Explanation: A moved block tells Terraform that an existing state binding has a new configuration address. The from expression must identify the old root address, while to must identify the resource’s exact new address inside the module. These addresses are unquoted references. Because the child resource has neither count nor for_each, its address has no instance key.

The unchanged input allows the plan to report only the address move. A plan must still be reviewed because moved preserves identity but does not suppress independent argument changes.

  • Reverse mapping attempts to move state from the new address back to the removed root address.
  • Quoted addresses are string values, not the static address expressions required by a moved block.
  • Indexed destination names an instance that does not exist because the child resource does not use count.

Question 57

Topic: State Management

A working directory was initialized with a local backend at state/legacy.tfstate. An administrator has already transferred and updated the authoritative state at state/shared.tfstate; the legacy state is now stale.

The configuration now contains:

terraform {
  backend "local" {
    path = "state/shared.tfstate"
  }
}

The existing .terraform metadata still references the legacy path. Complete the repair so Terraform accepts the new backend configuration without copying the legacy state.

____
terraform plan

Which command should replace ____?

Options:

  • A. terraform init -backend=false

  • B. terraform init -reconfigure

  • C. terraform init -upgrade

  • D. terraform init -migrate-state

Best answer: B

Explanation: Terraform caches backend configuration in the working directory’s .terraform metadata. After backend settings change, terraform init -reconfigure discards the cached backend association and initializes the backend currently declared in configuration. It does not attempt to copy state from the previously configured backend.

Here, the destination already contains the authoritative state, while the old path contains a stale snapshot. A state migration is therefore neither needed nor intended. After reconfiguration, a fresh plan can compare the configuration with the state at the new path. The key distinction is whether Terraform must transfer state or merely accept an already-completed backend change.

  • Upgrade dependencies changes allowed provider and module selections but does not resolve backend migration intent.
  • Migrate state treats the stale legacy state as the source for transfer, conflicting with the stated authoritative destination.
  • Disable backend initialization skips backend setup and leaves the changed backend metadata unresolved.

Question 58

Topic: HCP Terraform

An organization uses Terraform to manage an internal service whose provider API is reachable only from a private subnet. HCP Terraform hosted workers have no route to that subnet.

  • The HCP workspace stores state and has a mandatory Sentinel policy set.
  • Every plan and apply must remain an HCP run with centralized history and policy enforcement.
  • The subnet allows outbound HTTPS to HCP Terraform but prohibits public inbound access.
  • Provider credentials are already available to the workspace.

Which execution approach satisfies all requirements?

Options:

  • A. Use remote execution, triggering hosted-worker runs from the private CI runner while retaining the existing HCP workspace controls.

  • B. Use remote execution with hosted workers while supplying the private API endpoint and credentials through sensitive workspace variables.

  • C. Use agent execution with an agent pool in the private subnet while retaining HCP-managed runs, state, history, and policies.

  • D. Use local execution on the private CI runner while retaining HCP-hosted state and relying on the workspace policy set.

Best answer: C

Explanation: HCP Terraform agent execution separates the Terraform process location from the hosted governance services. An agent in the private subnet initiates an outbound connection to HCP Terraform, receives work, and runs Terraform where the provider API is reachable. HCP Terraform still manages the run queue, state, run history, and Sentinel policy checks.

Triggering remote execution from a private CI system does not move execution into that network; a hosted worker still performs the provider operations. Local execution can use HCP Terraform for state storage, but its operations do not become HCP-managed runs subject to the workspace’s normal run and policy workflow. Sensitive variables provide and redact values, but they do not establish network connectivity.

  • Private CI trigger fails because the initiating host does not determine where a remote run executes; the hosted worker still lacks a route.
  • Local execution reaches the API but bypasses the required HCP-managed run history and Sentinel enforcement workflow.
  • Sensitive variables can provide endpoint details and credentials, but they cannot give a hosted worker private network access.

Question 59

Topic: Core Workflow

A local Terraform CLI apply of a saved plan partially fails. The backend state update succeeds, and the state lock is released.

Evidence (provider-neutral resource labels):

example_network.main: Creation complete
example_server.app: Creation complete
example_monitor.app: Error 403 before creation

State:  example_network.main, example_server.app
Remote: network exists, server exists, monitor absent

The monitor credential is corrected, and no configuration or remote objects otherwise change. Which TWO statements correctly describe recovery from this partial apply?

Options:

  • A. The network and server should be imported before running another plan.

  • B. A fresh normal plan should propose creating only the missing monitor.

  • C. The network and server retain their existing managed state bindings.

  • D. A refresh-only apply should create the monitor while preserving existing objects.

  • E. The original saved plan should be reapplied to resume with the monitor.

Correct answers: B and C

Explanation: Terraform apply is not transactional and does not automatically roll back successful operations after a later failure. Here, both completed objects were persisted in state and confirmed remotely, so their bindings should be preserved. After correcting the monitor credential, the operator should create and review a fresh normal plan. Assuming no other drift, that plan should propose only the missing monitor.

The previous saved plan was created against an earlier state snapshot and may be rejected as stale. Import is unnecessary for objects already bound in state, while refresh-only mode updates state to match remote observations without creating the missing object.

  • Reuse the saved plan fails because successful creations changed state after that plan was produced.
  • Import existing objects is unnecessary because the network and server already have state bindings.
  • Use refresh-only apply fails because refresh-only mode does not perform configuration-driven creation.

Question 60

Topic: Configuration

A root module passes shared, environment, and component settings to a child module. For overlapping keys, precedence must be shared < environment < component.

module "service" {
  source = "./service"

  shared = {
    owner    = "platform"
    tags     = { team = "core", tier = "standard" }
    settings = { retries = 2, logging = "info" }
  }
  environment = {
    owner    = "production-ops"
    tags     = { environment = "prod", team = "operations", tier = "critical" }
    settings = { retries = 3, logging = "warn" }
  }
  component = {
    owner    = "payments"
    tags     = { team = "payments" }
    settings = { retries = 5 }
  }
}

The child module contains:

variable "shared" { type = any }
variable "environment" { type = any }
variable "component" { type = any }

locals {
  top_level = merge(var.shared, var.environment, var.component)
  effective = ____
}

local.effective must retain owner = "payments", all three tag keys, and both setting keys. Which expression should replace ____?

Options:

  • A.

    merge(local.top_level, {
      tags = merge(var.shared.tags, var.environment.tags, var.component.tags)
      settings = merge(var.shared.settings, var.environment.settings, var.component.settings)
    })
    
  • B.

    merge(local.top_level, {
      tags = merge(var.shared.tags, var.component.tags)
      settings = merge(var.shared.settings, var.component.settings)
    })
    
  • C.

    merge({
      tags = merge(var.shared.tags, var.environment.tags, var.component.tags)
      settings = merge(var.shared.settings, var.environment.settings, var.component.settings)
    }, local.top_level)
    
  • D.

    merge(local.top_level, {
      tags = merge(var.shared.tags, var.component.tags, var.environment.tags)
      settings = merge(var.shared.settings, var.component.settings, var.environment.settings)
    })
    

Best answer: A

Explanation: Terraform’s merge function is shallow: later maps replace earlier values at the same key rather than recursively combining nested maps. The initial top-level merge correctly selects owner = "payments", but its tags and settings values contain only the component’s nested maps. Each nested map must therefore be merged separately using the same shared, environment, component precedence. A final merge places those reconstructed nested maps over local.top_level, preserving the correct owner while producing all required nested keys.

Changing either the inner precedence or the outer merge order causes later values to replace the required result.

  • Environment applied last gives environment values precedence over component values, producing the wrong team and retry count.
  • Top-level map applied last replaces the reconstructed nested maps with the component-only maps already stored in local.top_level.
  • Environment omitted loses environment-only values and retains shared defaults where environment overrides are required.

Review your attempt

The questions are mixed across topics. Use the label on each question to record your results.

Topic labelCorrectMissed or guessed question numbers
Infrastructure as Code___ / 4___
Terraform Fundamentals___ / 6___
Core Workflow___ / 11___
Configuration___ / 14___
Modules___ / 6___
State Management___ / 7___
Infrastructure Maintenance___ / 5___
HCP Terraform___ / 7___

This raw score is a practice result, not an official pass prediction. An immediate repeat may test answer memory. Use the blueprint to locate a gap, the study plan to organize focused review, and official resources to check unfamiliar behavior.

If a question seems incorrect or unclear, email support@masteryexamprep.com with the page URL, question number, and the detail you want reviewed.

Continue in the web app

Use IT Mastery for interactive Terraform Associate (004) practice with mixed sets, timed mocks, topic drills, explanations, and progress tracking.

Try Terraform Associate (004) on Web