Crafting a Consistent Naming Convention for my Terraform Configurations

Establishing a consistent, yet flexible naming convention for your Terraform configurations is essential for maintaining a high level of readability, organization and collaboration. As your infrastructure grows in size and complexity, it becomes increasingly difficult to keep track of all the resources, variables, and modules. In this post, I will share my findings on the different aspects of creating a decent naming convention for Terraform. I will also introduce the naming convention I follow myself and explain how it can help you and your team members managing uncertainty and complexity.

I recently worked on a project that involved managing a large number of Terraform configurations, that I developed over the past few months and finally assembled togehther. Although it does not necessarily leads to a significant downside for now, the lack of a naming convention made it challenging to understand and remember the purpose of each resource and its relationship with other resources. Did I mean to use this variable here or there? So does the variable name subnet_id refer to the subnet of the private endpoint or is it the subnet of another resource?

The lack of a naming convention also made it difficult to onboard new team members and troubleshoot errors. When I introduced new team member to the project, they had to spend a some time to understand the intention of certain variables. This slowed down the onboarding process and made it difficult for them to contribute to the project. Apart from that it will be difficult to identify the root cause because of the inconsistent naming conventions.

In this blog post, I’ll share my findings on the different aspects of creating a decent naming convention for Terraform. I’ll also introduce the naming convention I developed and explain how it can help you and your team members maintaining your complex Terraform configurations.

A naming convention is a structured set of rules that guides the way you name resources, variables, and modules within your infrastructure-as-code projects. This convention serves as a navigational map, allowing both you and your team members to swiftly discern the intent of each resource and its interconnections. By providing a systematic approach to naming, it streamlines the process of error diagnosis and accelerates the integration of new team members. In essence, a well-crafted naming convention fosters clarity, cohesion, and efficiency across your project, enhancing not only readability but also collaborative effectiveness. (own definition)

Why Naming Conventions Matter in Terraform

In the dynamic landscape of infrastructure-as-code (IAC) projects, the role of naming conventions are far more than simple lexical choices. It emerges as a cornerstone, shaping the very essence of how projects are conceived, developed, and maintained. This pivotal role is underpinned by three cardinal virtues: readability, maintainability, and collaboration.

Readability

At the heart of every meticulously crafted naming convention lies the profound goal of enhancing readability. In a labyrinth of interconnected resources, variables, and modules, intelligible names act as beacons, illuminating the purpose and role of each element. Imagine deciphering a sprawling IAC configuration without consistent, meaningful nomenclature. It’s akin to navigating a foreign city without a map, leaving you disoriented and hampering comprehension.

A well-structured naming convention constructs a lexicon that transcends the code itself. It empowers developers to instantly glean insights into a resource’s function, be it a Virtual Private Cloud (VPC) or an application server instance. This clarity not only accelerates development but also eases troubleshooting when issues arise. An intelligible name can provide valuable context and cut through the fog of complexity, expediting the path to resolution.

Maintainability

The longevity of any IAC endeavor hinges on maintainability, and naming conventions are pivotal architects of this sustainability. As projects evolve, personnel transition, and knowledge permeates, a systematic nomenclature becomes an anchor. Variables, outputs, and modules intertwined with meaningful labels transcend personnel changes, ensuring continuity and reducing the learning curve for newcomers.

Consider a scenario where a key contributor moves on from the project. A well-structured naming convention is a torchbearer, enabling their successor to seamlessly comprehend the intricacies of the architecture. Furthermore, when updates or modifications are needed, an intelligible naming convention empowers developers to discern relationships and dependencies, mitigating the risk of unintended side effects.

Collaboration

When you’re working with a team, using a common language is crucial. In my opinion, naming conventions are just as important as the documentation or even the Terraform code itself. It helps everyone understand without needing to ask a lot of basic questions. This is especially important when you’re not the only one working on the project. And even if you are, you’ll probably need to revisit your code in the future. A naming convention makes it easier to understand what you’ve built and why you built it that way.

It’s not just about your team, though. Think about when you need to show your work to others, like your manager or your coworkers. They might not be familiar with Terraform, but they’ll still need to understand what you’ve built. A clear naming convention makes it easier for them to understand what you’ve built. It also makes it easier for them to give you feedback on your work.

Naming conventions are just like writing meaningful documentation: It might not be the most glamorous part of your job, but they’re like the practical tools that keep your project organized, understandable, and smoothly collaborative. They’re just like the name tags that make sure everyone knows who’s who at a party. They’re not the most exciting part of the party, but they’re essential for making sure everyone has a good time.

A Naming Convention that works (for me 😉)

I have been working on a few minor to major projects that involved managing a large number of Terraform configurations. While I applied the best practise to modularize my Terraform configurations, I did not have a consistent naming convention in place at first. This resulted in a lot of confusion and a bit of frustration when I tried to glue my modules together in accordance to different project requirements. As I mostly developed these by myself, I did not have to worry about the naming convention too mush. But I found it suprising to see, that although I developed them myself, I did not understand the intention of certain variables after a few weeks. Oops.

First of all, I want to emphasize that there is no one-size-fits-all naming convention. The naming convention I developed is based on my personal experience and the best practices I learned from googling a lot. You are free to adapt this convention to your needs. These are the rules I follow when I develop my Terraform configurations:

  1. Enhanced Comprehension: Developing a naming convention that fosters clear understanding of the purpose of each variable and module.

  2. Efficient Error Identification: Create a naming convention that facilitates quick identification of the root cause of errors within the configuration.

  3. Seamless Onboarding: Design a naming convention that enables smooth onboarding of new team members, minimizing the time needed for them to grasp the intention behind specific variables.

  4. Collaboration Enablement: Ensure that the naming convention promotes collaboration, allowing team members to contribute effectively to the project without facing unnecessary hurdles in understanding variable intentions.

This can be achieved by implementing a consistent, understandable naming convention across all projects. The convention should be easy to understand and follow, and it should be flexible enough to accommodate future changes. It should also be easy to adapt to different projects, providers and environments.

General Naming Conventions

Lowercase Enforce lowercase naming convention for all resources, variables, and modules. This is a common convention in Terraform and should be followed. In my configurations, I either use lowercase or capslock for naming my resources, variables, and modules. I use capslock to highlight the origin of a asset. You can see an example later in this article. (e.g. I/O Variables)

Never use camelCase or PascalCase. Please. Just don’t.

Special Characters In Terraform, there is the possibility to use hyphens and underscores. In my experience, I only use hyphens for naming my projects and modules. Since the resource names in Terraform are already separated by underscores, it is some kind of standard to use underscores for the naming of resources as well as variables. Try to avoid using special characters like !@#$%^&*(). in your remote naming convention, for example in your cloud provider. Introducing e.g. a dot in your cloud provider naming convention can lead to some unexpected behaviour.

Abbreviations Also, I want to emphasize that you should not use odd abbreviations in your naming convention. This is a common mistake I see in a lot of projects. Abbreviations are not always clear and can lead to confusion. For example, if you use vn as an abbreviation for virtual network, it is not clear at first. Use common abbreviations where applicable. In this case, it would be vnet.

Project and Module Naming

Let’s start with the project naming itself. I like to use the following naming convention for my projects:

<project-name>-<provider>-<environment>

This naming convention is based on the requirement Terraform has in order to publish a module to the official Terrafrom registry:

  • The project name should be unique and should not contain any special characters.
  • The provider name should be the name of the main provider you are provisioning to. This gives you the flexibility to already set up your projects for possible multi-cloud deployments. In my recent projects, I have been working with Azure and a few on GCP. In my role as a Cloud engineer, it is also possible that I have to implement solutions also in AWS. So I like to keep my projects flexible enough to be able to deploy to different (cloud) providers by default.

  • The environment name should be the name of the environment you are deploying to. This can be either a dev, test, prod aproach * or a more specific naming convention like a customerID, department or something else.

* I personally tend to use workspaces for the dev, test, prod separation. It is generally advised, to define your config in a way, that it can be used for all three tiers. According to this practise, the only difference should be the .tfvars file in use. But I wanted to include this option as well.

Project and Module Directory Structure

For the sake of consictency I like to use the following directory structure for my modules. In addition to the generally used structure, I like to include a data.tf file, that contains all the data sources I am using in my module. This makes it easier to identify the data sources used in the module. It also enforces a consistent deployment by enforcing the usage of certain resources, just like a VNet, Subnet or at least a resource group. I also like to include a README.md file, that contains a short description of the module and the variables used. It may also be used to document the usage of the module in case there is no centralized directory for all terraform configurations, such as a registry. By the way, there are ways to automatically check if a PR contains changes to the README.md file. This way you can ensure that the README.md file is always up to date - or has been at least reviewed by a team member.

A example and test directory is also included. The example directory contains a few examples on how you, the creator, intended to use the module. The test directory contains a few tests that can be used to test the module. I like to use the Terratest framework for this purpose. More on this in a later post.

This is the directory structure I like to use, note that I use the plural form of the type of resources:

<module-name>
├── data.tf
├── main.tf
├── outputs.tf
├── variables.tf
├── README.md
├── examples
│   ├── example1.tf
│   ├── example2.tf
│   └── example3.tf
└── tests
    ├── test1.tf
    ├── test2.tf
    └── test3.tf

Variable Naming

All the trouble initially started when I tried to use a set of modules together. While I used variables like base_rg in one module, I unintentionally renamed a variable as common_rg in another module. This results in unnecessary confusion for me and possible user of my modules. So I decided to use a consistent naming convention for my variables:

Common Variables

I refer to variables that are used in multiple modules as common variables. Typically, these variables are used to define the name of a resource group, a location or a prefix I am referencing in data.tf files. I like to use the following naming convention for these variables:

<provider>_<resource>_<purpose> or: az_rg_name (for a pre-existing Azure resource group name)

Input and Output Variables

Input variables are the most critical variables in my opinion. They are used to connect modules together and are directly used. I like to use the following naming convention for these variables:

<SERVICE-NAME>_<resource>_<purpose> or: AML_subnet_name (A Subnet name used for the Azure ML services (e.g. compute instances); Note the Case sensitivity)

Output variables can be used for informational purposes or to connect modules together. I like to include the module name so that it is clear which module the output variable belongs to. This leads me to the following naming convention for output variables:

<MODULE>_<SERVICE-NAME>_<resource>_<purpose> or: EZBASTION_AML_subnet_id (The Subnet id of a compute instance in Azure ML Studio; Note the Case sensitivity)

Tfvars Files

Every terraform configuration should have at least one .tfvars file. While it is possible to use the default terraform.tfvars file, you should not use this for some reasons:

  1. terraform.tfvars file is a special file that is loaded automatically by Terraform. This can lead to unexpected behaviour, especially when you are working with multiple environments.
  2. One can not be sure that the variable definitions in the terraform.tfvars file are intended for use in your target environment. Is it dev? Is it test? Is it prod? You can not be sure unless you check the content of the file. This can lead to unexpected behaviour as well. What variable set exactly is dev?
  3. You can not personalize the name of the file. Imagine your are a team of developers, each with a different focus. One is responsible for the ML part, one for the compute part and one for the storage part. Separating these individual configurations, especially in testing environments, can be cumbersome.

For these obvious reason I like to this important file to be clearly named. Even though you might be developing on your own or in a team of two, always think about the future. Here are my thoughts on this:

<project>_<env>_<set> where set can be default or the initials of the developer. For example: ezbastion-dev-default.tfvars or ezbastion-dev-jweins.tfvars

This way each developer and each environment has its own set of variables at hand if needed!

Enforcing the Naming Convention

Naming conventions are just like documentation: They are only useful if they are used. So how can we enforce the usage of the naming convention? There are a few ways to do this, either by using Policy as Code and/or by using a CI/CD pipeline. I will cover both in a later post. For now, I will just mention a few solutions to enforce the naming convention:

  • Use a linter like TFLint
  • Use Policy as Code to check the naming convention. (e.g. Sentinel or Open Policy Agent)
  • Use CI/CD pipelines to check the naming convention, with a linter, PoC and/or a custom script

Wrap Up

In this blog post, I discussed the importance of naming conventions in Terraform configurations. I also proposed a naming convention for Terraform configurations and discussed the rationale behind it. I hope that this blog post can be useful and help you implement a naming convention for your Terraform configurations. In short, I covered the following: clear names enhance code readability, while using setting general conventions is crucial. Additionally, I highlighted the significance of meaningful names that convey resource purpose, environment, and attributes, promoting easy understanding and reusability without extensive code analysis.

Thanks for working your way through this interesting, yet important topic that is often neglected. If you have any questions, feedback or suggestions, please reach out to me via email or linkedIn. I would love to hear your thoughts on this topic.

If you want to show your appreciation, please consider liking and sharing my blogpost on linkedIn. I would really appreciate it! Thanks for reading! 🤓👍🏽

References