Terraform Project Structure
Introduction
The discipline and techniques of software development can also be used when designed the architecture and structure of Terraform projects. Terraform projects have a similar lifecycle to development projects, and some of the structures we see in development projects can be applied to Terraform deployment projects.
The principles that we recommend adopting are :
- it must be possible for files to have different versions in different environments
- it should not be necessary to store sensitive credentials in text or readable files
- it should be easy to promote versions through the deployment lifecycle with CI/CD tools
- it should be possible to build applications and/or environments individually, without affecting other applications/environments
- all terraform code must be strongly version-controlled and kept in a source-code control system
- Appropriate steps should be taken to limit the damage of manual errors affecting more than single environments (aka limiting the “blast-radius”)
- The code structure should support the management of some resources that are designed to be shared across many applications and environments - e.g. DNS services, external connectivity, core routing
Basics
The basic unit of a Terraform project is a simple folder containing Terraform script files :
Project/
main.tf
network.tf
variables.tf
terraform.tfvars
… etc
Within this it is common to structure large projects with modules
Project/ main.tf network.tf variables.tf terraform.tfvars webserver_module_ variables.tfserver.tf outputs.tf database_module_ variables.tfdatabase.tf outputs.tf
Multi-environment
The next step is to support multiple environments. While it is tempting to try to avoid copying files between environments, we must recognise that any Terraform project file might need to be at a different version between, for example, dev and test. Our recommendation here is to copy all of the Terraform files to each environment, but to control that copying through a CI/CD tool which is also integrated with source code control (e.g. git). This allows the Terraform code to be properly versioned and promoted between environments.
Project/
dev/
main.tf
network.tf
variables.tf
terraform.tfvars
webserver_module_/
variables.tfserver.tf
outputs.tf
database_module_/
variables.tfdatabase.tf
outputs.tf
test/
main.tf
network.tf
variables.tf
terraform.tfvars
webserver_module_/
variables.tfserver.tf
outputs.tf
database_module_/
variables.tfdatabase.tf
outputs.tf
Multi-App
In the above model, each Project maps to an application and has a dedicated set of environments. However for small and medium sized apps, it might be required that they all share the same environment (e.g. same VCN, subnets, etc). In this case, it is straightforward to drop in an additional layer in the hierarchy to support this :
Project/
dev/
app_1_
main.tf
network.tf
app_2
main.tf
network.tf
In this way, app_1 and app_2 can be managed independently - e.g. they can be rebuilt without affecting each other.
Shared Modules
Above, we described models that are used to structure the code within complex projects. Modules can also be shared across applications, environments and across the organization (we describe this in more detail in the “code re-use with modules” section).
For modules that are intended to be shared widely, then we recommend that those modules are shared through a local shared folder, a Terraform registry or a Git repository. More details on how to do this can be found in the [Terraform documentation].
When shared modules are referenced, the reference should always include a specific version number. It may be necessary for different versions of modules to be referenced from different environments for the same application (e.g. the system test environment may use 1.17 of a shared module, while version 1.18 may be being used in a dev environment).
Shared Services
In addition to the deployment of applications, Terraform can also be used to mage the provision of services designed to be shared across many environments and applications. Examples of this might include :
- DNS services
- Edge network routing
- management and monitoring
- development services (e.g. CI/CD tools)
In the vast majority of cases, these can be managed through the same projects structures as outlined above.
Production Security
The structure described above is primarily intended for use in the fast-changing environments of the non-production world. While it could be easily extended with another environment directory for production, this introduces substantial risk of user error that might impact a production service.
For this reason we recommend an ‘air-gap’ between non-prod and prod, and that appropriate sign-off and security processes are inserted between those environments. In effect, this means that it is likely that the production Terraform scripts will be hosted on a different server