[Terraform] Refer to resource information described in different tfstates
table of contents
My name is Teraoka and I am an infrastructure engineer.
There is a data source called terraform_remote_state that
is useful when writing Terraform HCL This time I would like to introduce this.
■What is terraform_remote_state?
I want to refer to information about resources described in different tfstates.
This is a convenient data source to use in such cases.
First of all, Terraform
has a file called tfstate that records resource information created when deploying with Terraform Apply
When building infrastructure with Terraform,
if there are multiple environments (stepping stone, production, development, etc.),
it is a golden rule to separate tfstate for each environment so that terraform apply does not affect other environments
When tfstate is separated
You may want to refer to
the information on resources created in the stepping stone environment In this case, "terraform_remote_state" is useful.
■When should you use it?
For AWS, one example is given below.
If we assume that when connecting to a server via SSH, we will go through a stepping stone server,
what will happen if we try to create a security group and rules in Terraform?
First, I think we will create a VPC and security group + rules for the stepping stone 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 placed in S3 with the name bastion.tfstate, but
this is okay because we are just adding the VPC and security group + permission rule from a specific IP.
So, what should be the settings for the "connected side" from the stepping stone?
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 the springboard server, you
need to allow access from the springboard security group, so
you need to specify the springboard security group ID in source_security_group_id.
However, since the stepping stone server and tfstate are separated in the backend description
, the security group ID cannot be referenced as is.
■How to use it
I would like to be able to refer to it when I find out that it is not possible.
First, you need to add output settings on the bastion.tf side.
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 need to output the value you want to reference in advance.
This time, we want to refer to the ID of the bastion security group, so
we will output aws_security_group.bastion.id.
In this state, you can refer to the value in terraform_remote_state from production.tf.
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" } }
Load the tfstate for the stepping stone saved in S3
using the data source terraform_remote_state The value can be referenced by the name specified in output, so
the value specified for source_security_group_id
will be data.terraform_remote_state.bastion.security_group_id.
■Summary
terraform_remote_state is used fairly often, so it's worth remembering.
There is no problem even if you separate tfstate with this!