How I Keep Terraform Simple
SPOLIER: The Configuration Doesn’t Have To Be DRY!
The term DRY stands for “Don’t Repeat Yourself’ and is a principal taken from The Pragmatic Programmer by David Thomas & Andrew Hunt who state “Every piece of knowledge must have a single, unambiguous, authoritative representation within a system”. This makes sense when you are creating a software program as why would you want to write the same functionality twice?
However, when writing a configuration, you want to keep all elements readable so whoever needs to interpret it in the future can easily understand the outcome the configuration deploys.
There’s a tweet by Cory House that I like to refer to when talking about complex Terraform configurations which pretty much sums it all up.
Code is like humor. When you *have* to explain it, it’s bad.
— Cory House (@housecor) November 13, 2013
Let me show you a quick example of a configuration to deploy an empty Azure resource group called ‘blog-example-rg’ that you want to be in the same location as another resource group named ‘blog-existing-rg’. There are 3 files in the working directory that are relevant here:
resource-group.tf
resource "azurerm_resource_group" "blog_example" {
name = "blog-example-rg"
location = data.azurerm_resource_group.blog_existing.location
}
data-sources.tf
data "azurerm_resource_group" "blog_existing" {
name = var.blog_existing_rg_name
}
variables.tf
variable "blog_existing_rg_name" {
type = string
default = "blog-example-rg"
}
By looking at resource-group.tf
you can see that the azurerm_resource_group
resource is used to create a resource group named blog-example-rg
.
From looking at this resource alone, you are not able to tell which location
this resource group will be created in as it gets this value from a data source. So, we need to check data-sources.tf
.
data-sources.tf
tells us that we need to check for a variable that populates the name of the data source.
variables.tf
finally gives us the name of the resource group the location will come from but doesn’t give us the location!
This is an example of keeping a configuration DRY by only specifying the value of the existing resource group name once, but it adds little value and takes longer to fully understand the configuration.
A simpler approach would be to specify the location in resource-group.tf
and not use a data source:
resource "azurerm_resource_group" "blog_example" {
name = "blog-example-rg"
location = "westeurope"
}
This has the benefit of keeping the configuration simple with an emphasis on readability and uses less lines of code.
In the real world we wouldn’t be building an empty resource group so I will expand on how I approach this using a simple virtual network configuration.
When building multiple resources I use a variable for the resource group location, mainly because I reuse this variable elsewhere in the configuration to help dynamically build resource names. This is a subject I will cover in another post, so I won’t include the syntax for the resource names here.
To build a virtual network, the azurerm_virtual_network
resource requires arguments for both resource_group_name
and location
, so instead of using the variable for location here and needing to specify a resource group name, I can simply use the attributes exported from the azurerm_resource_group
resource and feed those values into the arguments for the virtual network resource.
My code would look like this:
variables.tf
variable "location" {
type = string
default = "westeurope"
}
resource-group.tf
resource "azurerm_resource_group" "blog_example" {
name = "blog-example-rg"
location = var.location
}
virtual-network.tf
resource "azurerm_virtual_network" "blog_example" {
name = "blog-example-vnet"
resource_group_name = azurerm_resource_group.blog_example.name
location = azurerm_resource_group.blog_example.location
address_space = ["10.0.0.0/16"]
}
This then has the benefit of only having to specify the location once and it cascading to all resources that use exported attributes.
Key Takeaways: Be explicit with your infrastructure code. Think about the readability of your code, as if it is not clear, you will struggle to manage it and may fail to spot security problems. Having to check multiple files to try and piece an abstracted configuration together over complicates the troubleshooting process and introduces unnecessary delays.