Learn how to design a scalable Terraform monorepo
- tfgen - We use tfgen to generate Terraform boilerplate code for our modules, like backend, provider configuration and variables
- tfenv - Terraform version manager, to switch to different version between modules automatically
infra-live
: Here you would separate your resources per environment, and inside this folder you can organize the resources in your own way. What matters here is that the pattern is known inside your organization and the people know how to deal with itmodules
: Here you would create your own reusable modules. Keep in mind that you can find lots of open source modules from the community, so my recommendation is to look over Github if someone already built a module for your use case. Common modules you can find out there, for example terraform-aws-vpc, covers pretty much everything you need to provision a VPC on AWS, so probably it is not worth building your own..tfgen.yaml
: This is a config file used by a tool I created to help us to create boilerplate code for our modules, increasing productivity and reducing risks of making mistakes during copy+paste operations. You can check it tfgen here.
.
├── README.md
├── infra-live
│ ├── dev
│ │ ├── networking
│ │ ├── s3
│ │ ├── security
│ │ ├── stacks
│ │ └── .tfgen.yaml
│ ├── prod
│ │ ├── networking
│ │ ├── s3
│ │ ├── security
│ │ ├── stacks
│ │ └── .tfgen.yaml
│ └── .tfgen.yaml
└── modules
└── my-custom-module
└── main.tf
Let's cover some patterns to organize the infra-live
folder, which will contain all the actual infrastructure, separated by environment. Considering an environment folder, you can organize it in several ways.
You can create individual folders for each resources in the cloud, so you would have something like this:
.
├── ec2
├── rds
│ └── my-database-1
├── iam
│ ├── roles
│ └── users
├── s3
│ └── my-bucket-1
└── vpc
You can create folders based on the resources type (compute, security, networking, etc):
.
├── compute
│ ├── my-asg
│ └── my-eks-cluster
├── security
│ └── iam
│ ├── roles
│ └── users
├── storage
│ ├── rds
│ └── s3
└── networking
├── vpc
└── vpc-peering
Using this pattern you'll have a mix of the previous two.
.
├── compute
│ ├── my-asg
│ └── my-eks-cluster
├── security
│ └── iam
│ ├── roles
│ └── users
├── stacks
│ ├── my-app-stack-1
│ └── my-app-stack-2
├── s3
│ ├── my-bucket-1
│ └── my-bucket-1
└── networking
├── vpc
└── vpc-peering
Let's talk about the stacks
folder. Sometimes you have an app that uses multiple cloud resources, like SQS Queues
, SNS Topics
, S3 Buckets
, so it could be confused to deploy these resources in different places, so maybe it makes sense to have a module just for this app. You could add it to the stacks
folder, and deploy all of them together. If you do that, makes sense to give all the resources some kind of prefix, ideally the name of the app that uses it.
This pattern can get trick if you have a resources that's shared between more than one app, for example a
SQS Queue
that have a consumer and a writer.
To organize your files and have a pattern across multiple modules, you can name your files like this:
_backend.tf
: Put your backend configuration here_dependencies.tf
: Whenever you need to query data from the state or the cloud, add it here_provider.tf
: Your providers configuration_vars_.tf|_variables.tf
: The variables declarationmain.tf
: The modules and resources declaration
Some anti-patterns I observed and heard about
In my opinion this is the most common anti-pattern. Some people may argument that having a module that deploys the entire infrastructure is a good thing and will be useful in a disaster recovery situation. But the problem is, that you probably never gonna need to do that. The operational overhead for keeping this structure will not be paid by the "benefit" it offers.
In addition, we have other problems by using this structure:
- All the
plans/applies
will take a long time to run, and it will just get worse as your infrastrcture grows. The-target
option should be used just in exceptional circunstances, as mentioned in the Terraform documentation. - The companies will grow with time, so will the need for deploying cloud resources and people contributing to the project. Having just one module will be
impracticable
. Velocity will be severely reduced. - High risk of messing something up.
So, if you see something like this, run show them this repo!
.
└── entire-infra-module
├── backend.tf
├── s3.tf
├── iam.tf
├── eks.tf
├── vpc.tf
└── rds.tf