Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 9 min read

Mocking terraform_remote_state in Terraform Tests (.tftest.hcl)

Quick answer

Mock terraform_remote_state in Terraform tests with override_data and mock_provider. Fix data.terraform_remote_state failures in .tftest.hcl unit tests for Terraform 1.7+.

  • #terraform
  • #testing
  • #mock_provider
  • #troubleshooting
Free toolkit

Fixing errors like this? Get 500 free DevOps AI prompts

500 copy-paste AI prompts for the stack you actually run — one PDF, free.

The Problem

You want to unit-test a module with terraform test, but the module reads outputs from another state file through a terraform_remote_state data source. As soon as the test runs, it tries to reach the real backend:

Error: Unable to find remote state

  on network.tf line 1, in data "terraform_remote_state" "network":
   1: data "terraform_remote_state" "network" {

No stored state was found for the given workspace in the given backend.

Or worse, it succeeds by connecting to a real S3/GCS/Terraform Cloud backend, which makes your “unit” test depend on live infrastructure, network access, and credentials. Neither is acceptable for a fast, hermetic test.

Why terraform_remote_state Breaks Unit Tests

A data "terraform_remote_state" block is not a normal provider data source. It is a special built-in that opens the configured backend and deserializes another configuration’s state. During terraform test, Terraform still evaluates that block, so it needs the backend to be reachable and the referenced state to exist.

That coupling defeats the purpose of a unit test. You cannot assert on a module in isolation if it first has to authenticate to a remote backend and read state written by a different pipeline. The fix is to replace the remote-state read with values you control, using the native mocking primitives introduced for .tftest.hcl files.

The Two Tools You Need

Terraform 1.7+ gives you two ways to intercept remote state:

  • override_data — replaces the computed values of one specific data block (including terraform_remote_state) with literals you supply. Surgical and explicit.
  • mock_provider — supplies fake data for a whole provider. terraform_remote_state belongs to the built-in terraform provider, so you can mock it broadly with mock_provider "terraform".

For remote state specifically, override_data is usually the cleaner choice because you almost always want to pin the exact outputs map the module consumes.

How to Mock terraform_remote_state

Step 1: The module under test

Assume network.tf reads a VPC ID from a network layer’s state:

data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "acme-tfstate"
    key    = "network/terraform.tfstate"
    region = "us-east-1"
  }
}

resource "aws_instance" "app" {
  subnet_id = data.terraform_remote_state.network.outputs.private_subnet_id
  ami       = var.ami_id
  instance_type = "t3.micro"
}

Step 2: Override the data block with override_data

Create tests/app.tftest.hcl. The override_data block targets the data source by its address and injects a fake outputs value:

mock_provider "aws" {}

run "instance_uses_private_subnet" {
  command = plan

  override_data {
    target = data.terraform_remote_state.network
    values = {
      outputs = {
        private_subnet_id = "subnet-0abc123"
        vpc_id            = "vpc-0def456"
      }
    }
  }

  variables {
    ami_id = "ami-000000"
  }

  assert {
    condition     = aws_instance.app.subnet_id == "subnet-0abc123"
    error_message = "app instance did not use the mocked private subnet"
  }
}

The mock_provider "aws" {} handles the AWS resources so the plan never contacts AWS. The override_data block handles the remote-state read so Terraform never opens the S3 backend. The whole run is offline.

Step 3: Reuse the override across every run

If most runs need the same fake outputs, hoist the override to the top level of the file so it applies to all runs instead of repeating it:

mock_provider "aws" {}

override_data {
  target = data.terraform_remote_state.network
  values = {
    outputs = {
      private_subnet_id = "subnet-0abc123"
      vpc_id            = "vpc-0def456"
    }
  }
}

run "default_placement" {
  command = plan
  variables { ami_id = "ami-000000" }
  assert {
    condition     = aws_instance.app.subnet_id == "subnet-0abc123"
    error_message = "expected the mocked subnet"
  }
}

Step 4: Run the tests

terraform test
tests/app.tftest.hcl... in progress
  run "default_placement"... pass
tests/app.tftest.hcl... tearing down
tests/app.tftest.hcl... pass

Success! 1 passed, 0 failed.

Using mock_provider “terraform” Instead

If you would rather not name every remote-state block, mock the built-in terraform provider and supply defaults through a mock_data alias. This is coarser, but useful when many blocks share the same shape:

mock_provider "terraform" {
  mock_data "terraform_remote_state" {
    defaults = {
      outputs = {
        private_subnet_id = "subnet-0abc123"
        vpc_id            = "vpc-0def456"
      }
    }
  }
}

Every terraform_remote_state read now returns those default outputs. Prefer override_data when different blocks need different values, and mock_provider when a single default is fine everywhere.

Common Pitfalls

  • Forgetting the outputs wrapper. Consumers read .outputs.<name>, so your fake values must nest under an outputs key, not sit at the top level.
  • Mismatched target address. target must be the exact block address, including any module.<name>. prefix if the data source lives in a child module.
  • Still hitting the backend. If a run bypasses the override, check that the override is at the right scope (run-level vs file-level) and that the address matches. A stray real read means the address is wrong.
  • Mixing plan and apply. For pure logic checks, command = plan is faster and needs no mock resource IDs. Use command = apply only when you must assert on computed apply-time values.

To generate these .tftest.hcl scaffolds quickly, the DevOps AI prompt library has ready-made prompts for Terraform test authoring.

Frequently Asked Questions

Can override_data mock a terraform_remote_state data source? Yes. Set target to the block address (for example data.terraform_remote_state.network) and put the fake state under an outputs key inside values. Terraform then skips the real backend read.

Do I need mock_provider if I use override_data? Not for the remote state itself. But you still need mock_provider "aws" (or your cloud) so the actual resources plan offline. override_data only replaces the one data block you target.

Where should the override go, in the run block or the file? Put it in a run block when only that run needs it, or at file scope to apply it to every run. File scope avoids repeating the same fake outputs across many runs.

Why does my test still contact S3? The override address probably does not match the data block, or the override is scoped to a run that is not executing. Confirm the full address, including any module. prefix, and that the run actually reaches the block.

Should I use plan or apply for these tests? Use command = plan for logic and wiring assertions; it is faster and fully offline. Reserve command = apply for assertions that depend on apply-time computed values. For more patterns, see the Terraform guides.

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.