For those un-initiated in Infrastructure-as-a-code, Terraform is a tool to manage your infrastructure elements as a code. Basically if you want to manage say Amazon Web Service (AWS) resources such as Ec2 instances, you can create those to your specification in a simpler way using Terraform. Terraform will manage provisioning, building and versioning all your such infrastructure elements. It’s a great tool that has become very popular to manage huge amounts of Public Cloud Vendor infrastructure elements with ease.
Just some time back we were also developing a tooling in Terraform that’ll allow us to create and “bootstrap” new AWS accounts with ease. These newly created account will have same stuff set up as bootstrapping processing, these will be overall controls that other Applications will be built on top of. E.g. Setting up VPCs, Networking, Basic Account restrictions etc.
I was put on to develop a small Terraform module that’ll serve as a basic lookup module that’ll provide common elements E.g. What is Engineering VPC, what is DevOps VPC, what are basic Security groups that can allow access to/from Corporate etc. While working on this module I did discover amazing that you can or CAN NOT do with Terraform. One such case was for a specific set of lookups as described below:
That last little condition has potential to break Terraform developer ;) Here is how my journey of discovering solution to this problem went:
In attempt 1, I tried simple thing as follows:
locals {
vpc_env = "test" // Your environment / account name
region = "us-east-1"
}
data "aws_vpc" "vpc" {
tags = {
Name = format("%s-vpc-%s-%s", local.region, "engg", local.vpc_env)
}
}
data "aws_vpc" "vpc_devops" {
tags = {
Name = format("%s-vpc-%s-%s", local.region, "devops", local.vpc_env)
}
}
output "vpc" {
value = data.aws_vpc.vpc.id
}
output "vpc_devops" {
value = data.aws_vpc.vpc_devops.id
}
I ran this script on two AWS accounts:
In case of dev account, script worked perfectly fine as follows:
siddharth_godbole$ terraform apply
provider.aws.region
The region where AWS operations will take place. Examples
are us-east-1, us-west-2, etc.
Enter a value: us-east-1
data.aws_vpc.vpc: Refreshing state...
data.aws_vpc.vpc_devops: Refreshing state...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
vpc = vpc-XXXXXXX
vpc_devops = vpc-YYYYYYY
It failed spectacularly in Test account as follows since Terraform expects resource “vpc_devops” to be present in this script:
siddharth_godbole$ terraform apply
provider.aws.region
The region where AWS operations will take place. Examples
are us-east-1, us-west-2, etc.
Enter a value: us-east-1
data.aws_vpc.vpc: Refreshing state...
data.aws_vpc.vpc_devops: Refreshing state...
Error: no matching VPC found
on main.tf line 12, in data "aws_vpc" "vpc_devops":
12: data "aws_vpc" "vpc_devops" {
By this time, it was obvious I was looking for something conditional, but first I have to know if DevOps VPC exists. One way I was able to figure out is by using another DataSource called aws_vpcs supported by Terraform’s AWS Provider. This datasoure basically looks up VPCs with a given filter and provides List of VPCs found, counting list if these VPCs can then be used to see if DevOps VPC exists or not.
Simple check then I was planning to do was if No. of VPCs is more than 1, we have deops VPC as well. Here is what I ended up creating:
locals {
vpc_env = "test"
region = "us-east-1"
}
data "aws_vpcs" "all_vpcs" {
filter {
name = "tag:Name"
values = [format("%s-vpc-%s-%s", local.region, "engg", local.vpc_env), format("%s-vpc-%s-%s", local.region, "devops", local.vpc_env)]
}
}
data "aws_vpc" "vpc" {
tags = {
Name = format("%s-vpc-%s-%s", local.region, "engg", local.vpc_env)
}
}
data "aws_vpc" "vpc_devops" {
count = length(data.aws_vpcs.all_vpcs.ids) > 1 ? 1 : 0
tags = {
Name = format("%s-vpc-%s-%s", local.region, "devops", local.vpc_env)
}
}
output "vpc" {
value = data.aws_vpc.vpc.id
}
output "vpc_devops" {
value = data.aws_vpc.vpc_devops.*.id
}
Couple things to notice in this script:
Running this script on previously failed Test account yielded result I was looking for:
siddharth_godbole$ terraform apply
provider.aws.region
The region where AWS operations will take place. Examples
are us-east-1, us-west-2, etc.
Enter a value: us-east-1
data.aws_vpcs.all_vpcs: Refreshing state...
data.aws_vpc.vpc: Refreshing state...
Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
Outputs:
vpc = vpc-XXXXXXXXXXXX
vpc_devops = []
You can create such resource dependency in Terarform 0.12 with ease, just that all derived outputs from “conditional expressions” have to use Splat Expression while accessing output variables from conditional resources / data sources.