Objective
By the end of this lab, you will have:
- Created a producer configuration with outputs.
- Created a consumer configuration that reads the outputs.
- Used
terraform_remote_stateto bridge the two configurations. - Refactored the producer and consumer together.
Requirements
- A Linux or macOS workstation with shell access.
- The Terraform CLI 1.9.x or later installed.
Scenario
You have a producer configuration that creates a file. You have a consumer configuration that reads the files metadata. The two configurations are separate. The producers outputs are the consumers inputs.
Tasks
Task 1: Create the working directory
mkdir -p ~/rb-remote-state-lab/producer
mkdir -p ~/rb-remote-state-lab/consumer
cd ~/rb-remote-state-lab
Task 2: Create the producer configuration
Create producer/main.tf:
terraform {
required_version = ">= 1.9.0"
required_providers {
local = {
source = "hashicorp/local"
version = "~> 2.5"
}
}
backend "local" {
path = "terraform.tfstate"
}
}
resource "local_file" "config" {
filename = "${path.module}/config.yaml"
content = "Configuration for the project.\n"
}
output "filename" {
value = local_file.config.filename
description = "The path to the configuration file."
}
output "size" {
value = local_file.config.content_length
description = "The size of the configuration file in bytes."
}
Task 3: Apply the producer
cd ~/rb-remote-state-lab/producer
terraform init
terraform apply
Verify the producer:
terraform output
The output shows the filename and size.
Task 4: Create the consumer configuration
cd ~/rb-remote-state-lab/consumer
Create consumer/main.tf:
terraform {
required_version = ">= 1.9.0"
backend "local" {
path = "terraform.tfstate"
}
}
data "terraform_remote_state" "producer" {
backend = "local"
config = {
path = "${path.module}/../producer/terraform.tfstate"
}
}
output "producer_filename" {
value = data.terraform_remote_state.producer.outputs.filename
description = "The filename from the producer state."
}
output "producer_size" {
value = data.terraform_remote_state.producer.outputs.size
description = "The size from the producer state."
}
Task 5: Apply the consumer
cd ~/rb-remote-state-lab/consumer
terraform init
terraform apply
The consumer reads the producers outputs.
terraform output
The output shows the producers filename and size.
Task 6: Modify the producer
cd ~/rb-remote-state-lab/producer
Edit producer/main.tf to change the file content:
resource "local_file" "config" {
filename = "${path.module}/config.yaml"
content = "Configuration for the project.\nversion: 1.1\n"
}
Apply:
terraform apply
The file is updated.
Task 7: Re-read the consumer
cd ~/rb-remote-state-lab/consumer
terraform apply
The consumers outputs are updated.
terraform output
The size has changed.
Task 8: Refactor the producer
Suppose the producer is renamed from config to settings.
Edit producer/main.tf:
moved {
from = local_file.config
to = local_file.settings
}
resource "local_file" "settings" {
filename = "${path.module}/config.yaml"
content = "Configuration for the project.\nversion: 1.1\n"
}
output "filename" {
value = local_file.settings.filename
description = "The path to the configuration file."
}
output "size" {
value = local_file.settings.content_length
description = "The size of the configuration file in bytes."
}
Apply:
cd ~/rb-remote-state-lab/producer
terraform apply
The state is updated. The real-world file is unchanged.
Task 9: Update the consumer
Edit consumer/main.tf to read the new resource:
output "producer_filename" {
value = data.terraform_remote_state.producer.outputs.filename
description = "The filename from the producer state."
}
output "producer_size" {
value = data.terraform_remote_state.producer.outputs.size
description = "The size from the producer state."
}
(The consumer does not need to change because the output names are the same.)
Apply:
cd ~/rb-remote-state-lab/consumer
terraform apply
The consumers outputs are unchanged.
Validation
The lab is successful if:
- The producer configuration has outputs.
- The consumer configuration reads the outputs.
- The two configurations are wired together.
- A refactor of the producer is reflected in the consumer.
Expected Outcome
At the end of the lab:
+---------------------------------+
| ~/rb-remote-state-lab/ |
| producer/ |
| │ ├── config.yaml |
| │ ├── main.tf |
| │ └── terraform.tfstate |
| consumer/ |
| ├── main.tf |
| └── terraform.tfstate |
+---------------------------------+
The producers outputs are consumed by the consumer. The two are wired together.
Cleanup
cd ~/rb-remote-state-lab/producer
terraform destroy
rm -rf .terraform .terraform.lock.hcl terraform.tfstate
cd ~/rb-remote-state-lab/consumer
terraform destroy
rm -rf .terraform .terraform.lock.hcl terraform.tfstate
The two configurations are the artefacts worth keeping.
What You Learned
You learned the cross-stack pattern:
- The producer has outputs. The outputs are the contract.
- The consumer reads the outputs. The consumer is wired
to the producer via
terraform_remote_state. - A refactor of the producer is reflected in the consumer.
The consumers
datablock is updated. - The two configurations are independent. The producer can be applied without the consumer.
- The output names are the contract. Renaming an output breaks the consumer.