[Terraform] Refer to resource information described in different tfstate

table of contents
My name is Teraoka and I am an infrastructure engineer.
One useful
data source when writing Terraform HCL is terraform_remote_state.
I would like to introduce it this time.
What is terraform_remote_state?
This is a convenient data source for when you want to refer to information about resources described in different tfstates
Terraform
has a file called tfstate that records the resource information created when deploying with terraform apply
When building infrastructure with Terraform,
if you have multiple environments (springboard/production/development, etc.),
it is a rule of thumb to separate tfstate for each environment so that terraform apply does not affect other environments
If you separate tfstate,
you may want to refer to
the information of resources created in the bastion In such cases, "terraform_remote_state" is useful.
■When to use it
Below is an example using AWS
If you are going to connect to a server via SSH via a bastion server,
how would you create a security group and rules using Terraform?
First, you would create a VPC and security group + rules for the bastion server.
bastion.tf
terraform { backend "s3" { bucket = "terraform-tfstate" key = "terraform-blog/bastion.tfstate" region = "ap-northeast-1" } } resource "aws_vpc" "bastion" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags { Name = "vpc-bastion" } } resource "aws_security_group" "bastion" { name = "bastion-sg" description = "for bastion server" vpc_id = "${aws_vpc.bastion.id}" tags { Name = "bastion-sg" } } resource "aws_security_group_rule" "prod_sg_rule" { security_group_id = "${aws_security_group.bastion.id}" type = "ingress" from_port = "22" to_port = "22" protocol = "tcp" cidr_blocks = "XXX.XXX.XXX.XXX" }
In the backend settings, tfstate is stored in S3 under the name bastion.tfstate,
but this is fine because it only adds a VPC and security group + an allow rule from a specific IP.
So, what about the settings on the "connected side" from the bastion?
production.tf
terraform { backend "s3" { bucket = "terraform-tfstate" key = "terraform-blog/prod.tfstate" region = "ap-northeast-1" } } resource "aws_vpc" "prod" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags { Name = "vpc-prod" } } resource "aws_security_group" "prod" { name = "prod-sg" description = "for production server" vpc_id = "${aws_vpc.prod.id}" tags { Name = "prod-sg" } } resource "aws_security_group_rule" "prod_sg_rule" { security_group_id = "${aws_security_group.prod.id}" type = "ingress" from_port = "22" to_port = "22" protocol = "tcp" source_security_group_id = "${What should I specify???}" }
If you want to allow SSH connections from a bastion server,
you need to allow access from the bastion's security group, so
you need to specify the ID of the bastion's security group in source_security_group_id.
However, since the backend description separates the bastion server and tfstate,
you will not be able to reference the security group ID in this state.
■How to use it
Now that I understand that this is not possible, I would like to be able to refer to it.
First, you need to add an output setting to bastion.tf.
bastion.tf
terraform { backend "s3" { bucket = "terraform-tfstate" key = "terraform-blog/bastion.tfstate" region = "ap-northeast-1" } } resource "aws_vpc" "bastion" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags { Name = "vpc-bastion" } } resource "aws_security_group" "bastion" { name = "bastion-sg" description = "for bastion server" vpc_id = "${aws_vpc.bastion.id}" tags { Name = "bastion-sg" } } resource "aws_security_group_rule" "prod_sg_rule" { security_group_id = "${aws_security_group.bastion.id}" type = "ingress" from_port = "22" to_port = "22" protocol = "tcp" cidr_blocks = "XXX.XXX.XXX.XXX" } ## Add output "security_group_id" { value = "${aws_security_group.bastion.id}" }
If you want to reference a value from a different tfstate,
you must first output the value you want to reference.
In this case, we want to reference the ID of the bastion security group, so
we will output aws_security_group.bastion.id.
In this state, you can reference the value from production.tf using terraform_remote_state
production.tf
terraform { backend "s3" { bucket = "terraform-tfstate" key = "terraform-blog/prod.tfstate" region = "ap-northeast-1" } } resource "aws_vpc" "prod" { cidr_block = "10.0.0.0/16" enable_dns_hostnames = true tags { Name = "vpc-prod" } } resource "aws_security_group" "prod" { name = "prod-sg" description = "for production server" vpc_id = "${aws_vpc.prod.id}" tags { Name = "prod-sg" } } resource "aws_security_group_rule" "prod_sg_rule" { security_group_id = "${aws_security_group.prod.id}" type = "ingress" from_port = "22" to_port = "22" protocol = "tcp" source_security_group_id = "${data.terraform_remote_state.bastion.security_group_id}" } ## Add data "terraform_remote_state" "bastion" { backend = "s3" config { bucket = "terraform-tfstate" key = "terraform-blog/bastion.tfstate" region = "ap-northeast-1" } }
Use the terraform_remote_state data source
to read the tfstate for the bastion stored in S3.
The value can be referenced by the name specified in output, so
the value specified for source_security_group_id
is data.terraform_remote_state.bastion.security_group_id.
■Summary
Since terraform_remote_state is used quite often, it's worth remembering.
Now there's no problem with separating tfstate!
1