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 :

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 :

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