DEV Community

Leon Nunes
Leon Nunes

Posted on • Originally published at kubesimplify.com on

A simple way to Structure your Terraform code

Basic Structure

Writing Terraform code is like connecting wires to a network switch(I'm not a Network Engineer), but you get the sentiment(we've all seen how bad wires can get).

When you start writing Terraform code everything is usually in the same folder, this might be good for projects that have a few instances or if you need to start your project quickly.

Your structure would look like this.

.
 backend.tf
 db-instance.tf
 iam.tf
 igw.tf
 provider.tf
 s3.tf
 subnets.tf
 variables.tf
 vpc.tf

Enter fullscreen mode Exit fullscreen mode

This is pretty good and straightforward for beginner projects and smaller projects, but slowly you will add more resources, and you'll soon be waiting for Terraform to finish refreshing all the 20 resources every time you make a change to your code. This is one of the reasons, the other reason is there is something called a blast radius, blast radius is basically a term that means how much of your Infrastructure can be accidentally deleted.

The last and main reason is to keep your sanity intact(I've read a few terraform horror stories).

Writing Better Code

What you should do is split your code into logical sections. For example, you have a "production" and a "non-production" environment. So you can create two separate folders, inside those you logically split the resources into sub-folders. Something along the lines of this.

production/
 backend
    dyanmodb.tf
    outputs.tf
    s3-backend.tf
    terraform.tf
    terraform.tfvars
    variables.tf
 cloudwatch
    main.tf
    terraform.tf
    terraform.tfvars
    variables.tf
 ecs
     main.tf
     outputs.tf
     terraform.tf
     terraform.tfvars
     variables.tf

Enter fullscreen mode Exit fullscreen mode

The files in each folder represent the Terraform Provider=>terraform.tf, the Terraform code => main.tf and the variables, you can also split those into various files.

Terraform Structure for ECS

When it comes to Elastic Container Service (ECS), I recommend doing something as follows, this makes sure your services are separate and nothing changes due to one service being changed.

 ecs
    main.tf
    terraform.tf
    terraform.tfvars
    variab
 services
     service1
     service2
     service

Enter fullscreen mode Exit fullscreen mode

Now you may wonder, since Terraform only works within one folder how would it work if you've split these resources. Well, there is something known as Terraform Remote State Data Source.

So to give you a gist of how this works, the outputs defined in the folder i.e in outputs.tf are accessible via the remote backend.

This is the network/terraform.tf file.

network/terraform.tf

terraform {
  required_providers {
    aws = {
      source = "hashicorp/aws"
      version = "4.4.0"
    }
  }
  backend "s3" {
    key = "network/terraform.tfstate"
    bucket = "Leos-tf-prod"
    encrypt = true
    profile = "My-custom-profile"
    region = "us-west-2"
    dynamodb_endpoint = "dynamodb.us-west-2.amazonaws.com"
    dynamodb_table = "production-tf-state"

  }
 }

Enter fullscreen mode Exit fullscreen mode

Note: Specifying the profile makes sure Terraform doesn't use the default profile.

Now I'll setup the resources using the AWS VPC module. Then If I need to access Data, such as VPC ID in another folder I need to define that using the Terraform output blocks in the outputs.tf.

# VPC
output "vpc_id" {
  description = "The ID of the VPC"
  value = module.vpc.vpc_id
}

output "vpc_cidr_block" {
  description = "The CIDR block of the VPC"
  value = module.vpc.vpc_cidr_block
}

Enter fullscreen mode Exit fullscreen mode

Now if I do terraform output I can see the values

$ terraform output

vpc_cidr_block = "20.0.118.0/17"
vpc_id = "vpc-03434c243d05599f"

Enter fullscreen mode Exit fullscreen mode

Let's move forward, now I need to access the same in my Elastic Container Service.

To do this I need to simply define a terraform_remote_state Data Source as such:

ecs/terraform.tf

data "terraform_remote_state" "network" {
  backend = "s3"
  config = {
    bucket = "Leos-tf-prod"
    key = "network/terraform.tfstate"
    region = "us-west-2"
    profile = "My-custom-profile"
  }
}

Enter fullscreen mode Exit fullscreen mode

Now if you've noticed, typing data.terraform_remote_state.network.vpc_cidr is going to cause you to type more or you need to remember stuff, to avoid this you can use locals as such

locals {
  vpc_id = data.terraform_remote_state.network.outputs.vpc_id
  private_subnets = data.terraform_remote_state.network.outputs.public_subnets
  public_subnets = data.terraform_remote_state.network.outputs.public_subnets
}

Enter fullscreen mode Exit fullscreen mode

Then in your folder, you can simply call the variable as such

local.vpc_id

Enter fullscreen mode Exit fullscreen mode

Benefits of this approach.

  • Reducing the blast radius, for example, if you accidentally run a destroy, a limited number of resources will be affected.
  • Improved security in case you need someone to work only on one part of the code.
  • Splitting the files reduces the time taken to apply changes.

In case you would like to continue the discussion, you can always reach out to me on Twitter or on LinkedIn, if you feel like following on Github you can also do that

That's it, thank you for reading.

Top comments (0)