DEV Community

Greg Oliver for CSE Dev Crews

Posted on • Edited on

Standardize resource names in Terraform scripts

This blog resulted from a customer development engagement. Want to read related blogs? Secure Azure as Code

When starting a Terraform project for an Azure architecture, it's easy to come up with useful names for the resources in your architecture. They usually look like "my-resource-group', "my-public-ip", "my-vm", "mystorageaccount", and so on. When the architecture grows, or elements of it scale out, it becomes harder to design useful names that meet all requirements. This blog is about a tool that helps with this task.

If you agree with the above and just want a link to the tool, here it is: terraform-provider-azurecaf.

Usage samples are fully implemented in the scenarios folder of the github repo.

Everyone else, read on.

Requirements that must be met

  • for each resource type, rules vary
    • length
    • accepted characters
    • accepted patterns (e.g. first character must be a lowercase alpha)
  • It must be possible to override generated names in special cases
  • It must be possible to generate globally unique names
  • Generated names must behave like any other resource in tfstate
    • names persist across 'terraform apply' runs as long as name resource definition remains the same
    • name resources can be destroyed, tainted, etc just like any other terraform resource

Additional nice-to-have features

  • a generated name should conform to a regular pattern that becomes familiar and instantly recognizable
  • when there are many resources in list, it should be possible to instantly recognize resource types from the name
  • clear and concise name generation code
  • when an architecture grows or resources scale out horizontally, name generation follows naturally
  • when testing permutations of resource properties, generating names is a very powerful enabling technique

Solution to the problem

The solution is a Terraform provider that generates resource names. Unsurprisingly, it meets all of the conditions above. Generated resource name configuration options include (in order of precedence):

  • name - overrides other options
  • slug - a few characters denoting the resource type
  • random - randomly generated chars
  • suffixes - an array of suffixes that are appended
  • prefixes - an array of prefixes that are pre-pended

All configuration options are defined in the provider repo.

The following examples are fully implemented in the scenarios folder of the github repo.

Generates and implements a resource group name similar to rg-xxxxxxxx (very simple example)

resource "azurecaf_name" "rg_name" {
  resource_type = "azurerm_resource_group"

  random_length = 8
}

resource "azurerm_resource_group" "rg" {
  name     = azurecaf_name.rg_name.result
  location = "uksouth"
}
Enter fullscreen mode Exit fullscreen mode

Generates and implements a log analytics workspace name (example of name override)

resource "azurecaf_name" "ws_name" {
  resource_type = "azurerm_log_analytics_workspace"

  random_length = 8
  suffixes      = ["local"]
  name          = substr(data.azurerm_subscription.current.display_name, 0, 44)

  # desired outcome = log-<sub name>-xxxxxxxx-local
  # length = 3 + 1 + (44) + 1 + 8 + 1 + 5 = max of 63 characters for log analytics workspace name
  # 44 is the max # of chars to get from the subscription name in order to get everything else into the generated name
  # because the "name" parameter is an override, if more characters are used, other portions of the generated name will be truncated
}

resource "azurerm_log_analytics_workspace" "ws" {
  name                = azurecaf_name.ws_name.result

  ...
}
Enter fullscreen mode Exit fullscreen mode

The resource name rules (such as the max length of 63 characters) for azurerm_log_analytics_workspace are in this file.

Generates multiple singleton names with a single azurecaf_name resource

resource "azurecaf_name" "names" {
  resource_type  = "azurerm_resource_group"
  resource_types = ["azurerm_lb", "azurerm_public_ip"]
  random_length  = 4
}

resource "azurerm_resource_group" "rg" {
  name     = azurecaf_name.names.result
  location = "uksouth"
}

resource "azurerm_public_ip" "pip" {
  name                = azurecaf_name.names.results["azurerm_public_ip"]

  ...
}

resource "azurerm_lb" "lb" {
  name                = azurecaf_name.names.results["azurerm_lb"]

  ...
}
Enter fullscreen mode Exit fullscreen mode

Generates a set of names per vm instance of a cluster of vms

resource "azurecaf_name" "per_instance" {
  count = var.number_of_servers

  resource_type  = "azurerm_windows_virtual_machine"
  resource_types = ["azurerm_network_interface"]

  name          = "websvr"
  random_length = 4

}

resource "azurerm_network_interface" "nic" {
  count = var.number_of_servers

  name                = azurecaf_name.per_instance[count.index].results["azurerm_network_interface"]

  ...
}

resource "azurerm_network_interface_backend_address_pool_association" "example" {
  count = var.number_of_servers

  network_interface_id    = azurerm_network_interface.nic[count.index].id

  ...
}

resource "azurerm_windows_virtual_machine" "vm" {
  count = var.number_of_servers

  name                = azurecaf_name.per_instance[count.index].result
  network_interface_ids = [
    azurerm_network_interface.nic[count.index].id,
  ]

  ...
}

Enter fullscreen mode Exit fullscreen mode

Top comments (0)