Terraform Deployment Structure
Using Terraform has become mainstream at this point and I would like to share with you, how I like to structure my deployments.
Table of Contents
- Introduction
- Disclaimer
- TL;DR
- General Files
- Environment Specific Files
- Folders
- Overview
- Other Files
- Final Thoughts
Introduction
When you write Terraform deployments, you have a specific goal. However, you might want to reuse code you wrote previously and this is where modules comes into play.
Modules play a big role if you want to become more efficient and create new deployments faster.
The goal is to create one generalized deployments, that can be used to deploy several environments.
Disclaimer
This is not a Terraform tutorial and it will not explain how Terraform works.
TL;DR
Check out my terraform repository on GitHub.
General Files
In your repo, create a folder called terraform.
This is the base for everything about your deployment.
I then proceed by creating separate files for all general information about the deployment (valid for any environment):
providers.tf
providers.tf contains the entire configurations of all necessary Terraform providers:
1 | provider "azurerm" { |
versions.tf
versions.tf contains all provider versions:
1 | terraform { |
backends.tf
backends.tf is where all backends are configured:
1 | terraform { |
variables.tf
At this point, you might have guessed it, variables.tf contains all necessary variables of the deployment:
1 | variable "tags" { |
main.tf
Last but not least, the main.tf is where it all comes together. From here, I call either my resource- or service modules.
One addition, I like to create a local section right in the root of the file to define all resource names. I then add an environment name in the .tfvars-file:
1 | locals { |
Environment Specific Files
Environment specific files must be created for each environment you want to deploy.
env_<env name>.sec.tfvars
Contains all secrets for interacting with the specified providers.
1 | client_id = "" |
env_<env name>_backend.sec.tfvars
Contains all secrets for interacting with the specified backends.
1 | storage_account_name = "" |
env_<env name>.tfvars
Contains all variables (except secrets and backend-config)
Folders
Within the terraform folder, create the following folders:
modules
Modules in Terraform make it easy to reuse code you wrote before and to share them with your colleagues.
The modules folder contains the following two sub-folders:
resource_modules
Resource modules represent one resource and one resource only.
Simplicity is key.
However, there are some exceptions, for example: when you provision a key vault and you want to deploy secrets to this very key vault within the same deployment.
For this, it would be ok to add an access policy for your terraform service principal in the same resource module - at least as far as I am concerned.
service_modules
Service modules describe a set of resources to represent a final service.
One example would be a basic virtual machine, because it consists at least of three resources:
- OS Disk
- Network Interface
- Virtual Machine
In this scenario, we would have three resouce modules for the above described resources and the service module calls them.
The service module is being called from the main.tf.
Overview
To visualize all this better, the following chart shows how it all works
Other Files
.gitignore
GitHub has a default terraform .gitignore-file, that needs to be extended by a few files.
1 | # Local .terraform directories |
I added exceptions for the following files:
- *.sec.tfvars
- *.tfplan
I like to run terraform with two stages or jobs in a pipeline:
- terraform init / terraform plan
- creates a run.tfplan file that will be consumed in the next stage/job
- terraform init / terraform apply
Final Thoughts
When you reuse modules, I would recommend to copy them each deployment individually, because you often need to adjust them and when you pull them from a central place, it could break other deployments - not very convenient đ
- create a repo with all your resource- and service modules
- update them if you need to for each deployment
- always write descriptions for variables
- donât overdo it with parameters
- not too few parameters either
- crate dependencies implicitly instead of explicitly
I hope you like it and if you have any suggestions or questions, please reach out on twitter @chrburmeister.