Mocking terraform_remote_state in Terraform Tests (.tftest.hcl)
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
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 specificdatablock (includingterraform_remote_state) with literals you supply. Surgical and explicit.mock_provider— supplies fake data for a whole provider.terraform_remote_statebelongs to the built-interraformprovider, so you can mock it broadly withmock_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
outputswrapper. Consumers read.outputs.<name>, so your fake values must nest under anoutputskey, not sit at the top level. - Mismatched target address.
targetmust be the exact block address, including anymodule.<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 = planis faster and needs no mock resource IDs. Usecommand = applyonly 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.
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.