I’ve been playing with terraform for a bit now and wanted to relay my experiences with the tool. Here are some interesting gotchas you may run into.

Plans are green until they’re not

Let’s say your plan goes smoothly, you’re about to stand up your awesome new architecture. Everything is nice and green. You run terraform apply and see something like this:

Error: Error launching source instance: VPCIdNotSpecified: No default VPC for this user
        status code: 400, request id: 67c7c54b-5e89-4b78-9a8a-69f56329dbf1

  on main.tf line 14, in resource "aws_instance" "web":
  14: resource "aws_instance" "web" {

Okay, this makes sense. My “web” instance(s) are don’t know the VPC. So maybe we can set a vpc_id = module.vpc.vpc_id for ec2 instances? wrong

Sometimes even though your plan looks fine the deploy will not work. Luckily terraform knows what has been deployed and can simply update the stuff it was missing if you can get your recipe just right.

Terraform relationship and ID puzzle

This time testing module.vpc.vpc_id plan fails :

Error: Unsupported argument

  on main.tf line 21, in resource "aws_instance" "web":
  21:   vpc_id = module.vpc.vpc_id

Being the studious developer we are we google to find that you can’t specify a vpc_id with an ec2 instance and it doesn’t get that from vpc_security_group_ids for some reason, however it does get the vpc from the subnet! Okay awesome.

Our plan said that module.vpc.aws_subnet.public[0] and module.vpc.aws_subnet.public[1] will be created because the plan shows us!

# module.vpc.aws_subnet.public[0] will be created
  + resource "aws_subnet" "public" {
      ...(omitted)
      + id                              = (known after apply)
      ...(omitted)
      + vpc_id                          = (known after apply)
    }

Knowing that we can use the new terraform 0.12 built in count.index and armed with this new information we immediately set subnet_id = module.vpc.aws_subnet.public[count.index].id and are confident we have solved the problem.

Our confidence is quickly destroyed as we run our next plan.

Error: Reference to undeclared output value

  on main.tf line 22, in resource "aws_instance" "web":
  22:   subnet_id = module.vpc.aws_subnet.public[count.index].id

An output value with the name "aws_subnet" has not been declared in
module.vpc.

So terraform can definitely plan a set of subnets but secretly hides where they are from us so that we don’t know. Through some more google foo we find the vpc module actually references them at module.vpc.public_subnets and since we have no idea what the underlying id is we can assume to use .id or possibly .subnet_id since the vpc itsels is referenced by .vpc_id.

Plan doesn’t like us again:

Error: Unsupported attribute

  on main.tf line 22, in resource "aws_instance" "web":
  22:   subnet_id = module.vpc.public_subnets[count.index].id
    |----------------
    | count.index is 1
    | module.vpc.public_subnets is tuple with 2 elements

This value does not have any attributes.

Since this feels close we try .subnet_id blindly and get nothing. Only to hope beyond hope that terraform actually tells us when we take off the last dot element and use subnet_id = module.vpc.public_subnets[count.index] but that makes plan go green and we now expect the deploy to fail with some error… except it works!???

Conclusion

I had even more gotchas for this post but I feel like this is a great overview of some issues you may face. Terraform is a very cool tool with a growing community but has a lot of what I like to call “intrinsic knowledge points” where once you’ve faced something 42 times you just know it and move on assuming everyone will figure it out. A good example of this is how everyone just knows that when your SNES cartridge doesn’t boot you blow on it.

Maybe you want a fun challenge, a new way of managing cloud architecture, or your current solution isn’t cutting it AND you don’t mind some pain. If so feel free to check Terraform out. It is “platform agnostic” that being said, plans do not work across platforms and you’ll have to have specific platform files and variables for each platform.

Hopefully 2.0 gets us closer to agnostic easy platform management tool it could be.