Navigate back to the homepage

Naming convention in Terraform

Frédéric De Smet
July 17th, 2021 · 1 min read

We’ve all been there we’ve set up multiple resources and tried to be as consistent as possible when it comes to naming conventions. This is not a simple assignment, this is where Terraform can help a litte.

The naming dilemma

Let’s take Azure naming as an example. This logic applies to multiple providers.

For Azure there are 3 topics we have to consider when choosing a name: rules, restrictions and recommendations.

Let us use a resource group as an example:

Rules

  1. Length should be between 1 and 90 characters
  2. Can include alphanumeric, underscore, parentheses, hyphen, period (except at end), and Unicode characters that match the allowed characters
  3. The name is case insensitive

Restrictions

  1. Should be unique withing a subscription

Recomendations

  1. Prefix with rg- to denote resource type
  2. Randomize the name to avoid naming collisions. This is usually done by adding a random string as suffix.
  3. Add valuable information in the name. Ex. environment name, org name, …

Terraform solution

Luckily Terraform has us covered by providing dynamic naming/functions which can hold all of these rules, restrictions and recommendations. (except the validation of the unique name).

1locals {
2 org = "fds"
3 prj = "naming"
4 env = "dev"
5 random_suffix = "rtfdg"
6}
7
8resource "azurerm_resource_group" "main" {
9 name = regex("^[-\\w\\._\\(\\)]+$",substr("rg-${local.org}-${local.prj}-${local.env}-main-${local.suffix}", 90))
10 location = "westeurope"
11}

This will result in rg-fds-naming-dev-rtfdg

Adding all of this code to each creation of a resource group does not seem to be very efficient. This is when Terraform Modules comes along.

All of this logic can be moved into a single naming module.

1terraform {
2 required_providers {
3 random = {
4 source = "hashicorp/random"
5 version = "~> 2.2"
6 }
7 }
8}
9
10variable "prefix" {
11 type = list(string)
12 default = []
13 description = "It is not recommended that you use prefix by azure you should be using a suffix for your resources."
14}
15
16variable "suffix" {
17 type = list(string)
18 default = []
19 description = "It is recommended that you specify a suffix for consistency. please use only lowercase characters when possible"
20}
21
22variable "unique_seed" {
23 description = "Custom value for the random characters to be used"
24 type = string
25 default = ""
26}
27
28variable "unique_length" {
29 description = "Max length of the uniqueness suffix to be added"
30 type = number
31 default = 4
32}
33
34variable "unique_include_numbers" {
35 description = "If you want to include numbers in the unique generation"
36 type = bool
37 default = true
38}
39
40resource "random_string" "main" {
41 length = 60
42 special = false
43 upper = false
44 number = var.unique_include_numbers
45}
46
47resource "random_string" "first_letter" {
48 length = 1
49 special = false
50 upper = false
51 number = false
52}
53
54locals {
55 // adding a first letter to guarantee that you always start with a letter
56 random_safe_generation = join("", [random_string.first_letter.result, random_string.main.result])
57 random = substr(coalesce(var.unique_seed, local.random_safe_generation), 0, var.unique_length)
58 prefix = join("-", var.prefix)
59 prefix_safe = lower(join("", var.prefix))
60 suffix = join("-", var.suffix)
61 suffix_unique = join("-", concat(var.suffix, [local.random]))
62 suffix_safe = lower(join("", var.suffix))
63 suffix_unique_safe = lower(join("", concat(var.suffix, [local.random])))
64 // Names based in the recomendations of
65 // https://docs.microsoft.com/en-us/azure/cloud-adoption-framework/ready/azure-best-practices/naming-and-tagging
66 az = {
67 resource_group = {
68 name = substr(join("-", compact([local.prefix, "rg", local.suffix])), 0, 90)
69 name_unique = substr(join("-", compact([local.prefix, "rg", local.suffix_unique])), 0, 90)
70 dashes = true
71 slug = "rg"
72 min_length = 1
73 max_length = 90
74 scope = "subscription"
75 regex = "^[a-zA-Z0-9-._\\(\\)]+[a-zA-Z0-9-_\\(\\)]$"
76 }
77
78 }
79 validation = {
80
81 resource_group = {
82 valid_name = length(regexall(local.az.resource_group.regex, local.az.resource_group.name)) > 0 && length(local.az.resource_group.name) > local.az.resource_group.min_length
83 valid_name_unique = length(regexall(local.az.resource_group.regex, local.az.resource_group.name_unique)) > 0
84 }
85 }
86}
87
88
89output "resource_group" {
90 value = local.az.resource_group
91}

Ps: this code is a little bit more complex for scalability reasons. At the end of this article you’ll find a reference to the repository where this snippet came from.

Usage

So we’ve created the module, now it’s time to use it in our own TF configuration. We have to create an instance of the naming module. The output of this module will be used as input for other TF resources.

1module "naming" {
2 source = "Azure/naming/azurerm"
3 suffix = [ "fds", "dev" ]
4}
5resource "azurerm_resource_group" "example" {
6 name = module.naming.resource_group.name ## results in rg-fds-naming-dev
7 location = "West Europe"
8}

if you want this to be unique for this module and not shared with other instances of this module you can use name_unique

1module "naming" {
2 source = "Azure/naming/azurerm"
3 suffix = [ "fds", "dev" ]
4}
5resource "azurerm_resource_group" "example" {
6 name = module.naming.resource_group.name_unique ## results in rg-fds-naming-dev
7 location = "West Europe"
8}

Here you can find the terraform naming module with most of the Azure Resouces already implemented. You can import this module in your own repository and add your own layer of requirements on top of it.

More articles from blog by Frédéric De Smet

VSCode Remote SSH

How to work with a Remote SSH in Visual Studio Code

December 1st, 2020 · 1 min read

Tips and tricks for passing the CKA exam

This article is about how I passed the certified Kubernetes Administrator (CKA) exam

November 8th, 2020 · 1 min read
© 2020–2021 blog by Frédéric De Smet
Link to $https://twitter.com/dsfrederic_Link to $https://github.com/dsfredericLink to $https://instagram.com/dsfredericLink to $https://www.linkedin.com/in/dsfrederic/