Article

How to Leverage HashiCorp Terraform Remote State

Referencing resource attributes in Terraform by HashiCorp is fairly straight forward—it just differs slightly depending on where the resource block is defined. If all of the resources are defined in the same .tf file, then it’s simply a matter of referencing the required attribute with the syntax ‘<type>.<name>.<attribute>’. Our root configuration folder looks something like this:

And here in an abbreviated version of our main.tf, we can see the assignment of “${aws_subnet.pri-subnet.id}” to the aws_instance resource’s subnet_id argument:

resource "aws_subnet" "pri-subnet" {
 vpc_id            = "${aws_vpc.vpc.id}"
 cidr_block        = "${var.pri_cidr_block}"
 availability_zone = "${var.pri_availability_zone}"
}
 
resource "aws_instance" "instance" {
 ami               = "${var.ami}"
 instance_type     = "${var.instance_type}"
 
 subnet_id         = "${aws_subnet.pri-subnet.id}"
       :
}

As we get better at writing Terraform and decide to modularize what we can, the aws_subnet resource is now defined in a module created for provisioning vpcs. Our folder structure now looks something like this:

And now in order to reference the private subnet id, we will have to make it available as an output from the vpc module. For example:

output "pri_subnet_id" {
 description = "Private subnet id"
 value       = "${aws_subnet.pri-subnet.id}"
}

In our main.tf, we can now see this change reflected in the assignment of the ‘subnet_id’ argument:

module "vpc" {
 source = "../modules/vpc"
 
       :
}
 
resource "aws_instance" "instance" {
 ami               = "${var.ami}"
 instance_type     = "${var.instance_type}"
 
 subnet_id         = "${module.vpc.pri_subnet_id}"
       :
}

So far, so good, right? However, what if the VPC is not provisioned by our main.tf? What if the network team is responsible for the Terraform configuration, which provisioned the VPC? Our file structure might now look something like this, where the network team has their own root configuration folder:

This is where terraform_remote_state steps in. Terraform remote state “Retrieves state data from a Terraform backend. This allows you to use the root-level outputs of one or more Terraform configurations as input data for another configuration”. It is referenced by the terraform_remote_state type and because it is a data source, it provides read-only access, so there is no danger of accidently interfering with the state file.

As mentioned, the state is retrieved via a Terraform backend. Backends warrant their own post, so we will get deeper into that another time, but in short, a backend refers to where you state is being stored and managed. This could be locally like my folder structure above suggests, which is the default, or it could be in an AWS S3 bucket, in Consul, Artifactory, etc. The full list of supported backends can be found here.

The following is an example of what the ‘terraform_remote_state’ data source configuration in our main.tf might look like for both a local backend and a remote backend:

Local Backend

data "terraform_remote_state" "vpc" {
 backend = "local"
 
 config {
   path = "../network/terraform.tfstate"
 }
}

Remote Backend

data "terraform_remote_state" "vpc" {
   backend = "s3"
   config {
       bucket  = "networking-terraform-state-files"
       key     = "vpc-prod01.terraform.tfstate"
       region  = "us-east-1"
   }
}

The full configuration reference can be found here, but the only required option is the ‘backend’ argument. The ‘config’ argument is where you specify the information used to locate the state file.

The update to the ‘subnet_id’ declaration in our ‘aws_instance’ resource is minor. Then, we simply add the new resource path:

resource "aws_instance" "instance" {
 ami               = "${var.ami}"
 instance_type     = "${var.instance_type}"
 
 subnet_id         = "${data.terraform_remote_state.vpc.pri_subnet_id}"
       :
}

Again, it is important to remember that the required subnet id—in this case ‘pri_subnet_id’—can only be references if it is configured as an output in the configuration which provisions it. So, how do you know what outputs are available? That’s a great question. In the case of our example, if you are also responsible for the configuration and management of the network resources, you can just check yourself. And if the subnet id is not configured as an output, you can configure it. But if another team manages it, then you will need to work with them to possibly receive—or maybe configure the information you require—access to location of the state files, etc.

Here’s another important thing to consider with regards to outputs in remote state: the fact that only root level outputs are accessible. Any outputs from sub modules will not be accessible unless your configuration propagates them up to the root modules outputs.tf.

Summary

Terraform state is obviously a key foundation block of Terraform, with the main focus being on management of your environment’s state. However, as we see from this post, it can also play an important role in the actual provisioning of your resources, along with a ton of other cool things you can utilize it for, too. For example, you can check out the terraform state command which provides a range of sub commands for not only managing your state, but for pulling information out of it which could be used elsewhere.

Check out AHEAD’s full library of HashiCorp resources here and subscribe to The LAB to receive demos like this one straight to your inbox.