Terraform: using modules for limiting the blast radius
1672876800

What is Terraform and why is it so great?

An easy way to think about Terraform is that it’s infrastructure-as-code (IaC). Infrastructure-as-code is a source-code description of your apps infrastructure resources that you want to easily manage


Infrastructure-as-code enables you to version control your backend resources, just like you would your application’s source code. You’ll have a clear track record of what was changed, who changed it, and when it was changed. No second-guessing with the cloud management console or throwing chairs around the office


This also means your infrastructure is self-documenting. You’ll always have the latest copy of the documentation (actual documentation can often drift away from reality) and it will all be in one place


HashiCorp Configuration Language (HCL)

You’ll spend most of your time writing in the HCL. It’s how you describe what the managed resources look like


The HCL is a nice lightweight, declarative language that looks very similar to a serialised JavaScript object (just substitute a colon with an equals symbol), where you have key/values wrapped around curly braces


The declarative nature of the language means you can focus on output (describe the final state/configuration) and Terraform will figure out the best approach to get resources to that state. Internally, Terraform uses the functional programming paradigm so there will be no unexpected side effects that would otherwise not deliver the expected resources (you don’t need to care about the internals of Terraform from a user point of view)


Providers

A provider is an API between Terraform and the upstream service offering the infrastructure resources. Most of the time, the upstream service offering the infrastructure resources is the public cloud (GCP, AWS, Linode and DigitalOcean have all built Terraform providers)


You don’t need to understand the internal workings of how a Terraform provider works, but you’ll need to be familiar with the resources that the provider exposes when you are writing HCL


The idea of modules

I’ve worked on multiple instances of Terraform that were shared by other people (i.e.: everyone worked in the same git repository that was tracking the HCL) and this created issues that I took the initiative to sort out


I needed a way to logically separate each “area” of work and limit the blast radius of other people's changes. When I’m working on the backend infrastructure for an application I’m developing, the last thing I feel like doing is having to figure out what these non-related changes are about and if they are OK to apply–I hate been slowed down like this


I discovered that modules provide this logical separation and you can exclusively work in your area of concern without affecting other people. Perfect!


Modules feel like functions

Modules, in Terraform, feel very similar to functions in that they can take input from parameters provided to them


Modules can also declare output variables that will be returned to the module one layer back (often, the module one layer back is the root module) that you’ll be able to reference


Defining a module

module "module-name" {
  source    = "./module-name-directory"
  parameter = value
}


You’ll need to define the input parameters within the module itself. This can almost be seen as the modules constructor (in the sense that these are the properties/variables that are going to be used)


If you are following the standard directory structure layout, you would define these input parameters in variables.tf and you can also declare the type

variable "parameter" {
  type = string
}


Limiting the blast radius and using the dot notation

Targeting modules is really simple. You can use the -target option on the CLI and specify the module you want to work with as a value to this option


Here is an example:

# terraform apply -target=module.module-name


You can further target resources in the module by using the dot notation of subsequent resources or even nested modules:

# terraform apply -target=module.module-name.resource-type.resource-name


Use an object store for the shared state

Don’t track the Terraform state locally in the repository. Doing this can lead to serious issues when multiple people are working on the project (constant conflicts and possible loss of management on the resources)


Something like this will work perfectly (ensure you wrap this around the terraform {} block in the root module):

backend "s3" {
  region = "ap-southeast-1"
  bucket = "terraform-state"
  key    = "terraform.tfstate"
}


A deeper explanation can be found in the documentation on how the backend configuration works


Alternatives to Terraform

Terraform is definitely most popular in terms of infrastructure-as-code but there are alternatives out there


CloudFormation | website

Native to AWS and not cloud agnostic. I’ve used CloudFormation before. It’s really easy to parse JSON, which is what my custom tooling did when we needed to do anything with the AWS infrastructure


My tooling made it pretty simple to make changes to the CloudFormation stacks: it asked the user for the environment they wanted to duplicate and a other specific information about the new stack and boom–all changed for them


Pulumi | website

I’ve never used Pulumi but it does seem like you can step the code through how it should go about doing the resource management (I’m not convinced that this is the right approach for IaC)


And I’m not sure how the pricing works either but it does seem pretty scary