Posts Terraform - Using Azure's uniquestring Function
Post
Cancel

Terraform - Using Azure's uniquestring Function

Or… “A complicated method for making unique resource names for the purposes of learning a bit about Terraform….”


I’ve been working with ARM Templates for a few years and have recently started to delve into Terraform. As a learning exercise, I’m taking some of the ARM templates that I’ve built over the last couple of years and refactoring them into Terraform files.

One of the patterns I’ve been following for a while is to use the “uniquestring” function in ARM to help generate names for Azure Resources - notably Azure Storage Accounts, as they must be unique throughout the whole of Azure. The “Uniquestring” function generates a value based on some seed values you pass to it. In my case, I usually pass resourcegroup().id and subscription().id which generates a string that is unique to the Resource Group within the Susbcription I’m deploying into.

1
"[uniquestring(resourceGroup().id, subscription().id)]"

By tacking the output of the uniquestring function to the end of a resource name:

  • You get a resource name that is unique throughout Azure so your deployment won’t fail
  • You have a name that is predictable name so (if you know the resource group and subscription ID) so you can “find” the resource within other ARM template deployments.

I use follow this pattern with Azure Storage Accounts used for diagnostics information. This way, resources within the same Resource Group share the same diagnostics resource group, rather than creating a separate storage account each time. I want to avoid this Storage Account “sprawl” by having resources share a Storage Account.

Random IDs in Terraform

Terraform has a resource that will generate various types of random ID (strings, integers, hex, etc). By referencing this random value within other

1
2
3
resource "random_id" "random_deployment_suffix" {
    byte_length = 4
}

You can refer the random_id generated here within other resources in the Terraform deployment.

1
"uniquestring${random_id.random_deployment_suffix.hex}"

In this example I’ve used the “hex” property, but the Random_ID resource has a bunch of data types it can return:

  • b64_std (String) The generated id presented in base64 without additional transformations.
  • b64_url (String) The generated id presented in base64, using the URL-friendly character set: case-sensitive letters, digits and the characters _ and -.
  • dec (String) The generated id presented in non-padded decimal digits.
  • hex (String) The generated id presented in padded hexadecimal digits. This result will always be twice as long as the requested byte length.
  • id (String) The generated id presented in base64 without additional transformations or prefix.

More info here: https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/id

In my use case, this won’t quite solve the problem as it is a completely random string that is generated. If I did the same within another deployment, a different random_id value would be generated. Rather than using the existing Storage Account for diagnostics, a new account would be created.

Deploying ARM Templates in Terraform and Capturing Output

Terraform can actually deploy ARM templates. It’s not something that is encouraged though, as it doesn’t know the state of the resources that were deployed, only that the ARM template deployment happened.

More info here: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/resource_group_template_deployment

You can capture the outputs from an ARM Template deployment though. So this was a way for me to use ARM’s “uniquestring” function from within Terraform.

I created an ARM deployment with no resources, just with an output from “uniquestring” based on the Resource Group and Subscription IDs. I’ve called the name of the ARM deployment “uniquestring-generator-“ followed by the random ID generated by Terraform. This is just to ensure the “uniquestring-generator” used by this Terraform deployment is unique from any others that may deploy into the same Resource Group in future.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
resource "azurerm_template_deployment" "uniquestring_arm" {
  name                = "uniquestring-generator-${random_id.random_deployment_suffix.hex}"
  resource_group_name = azurerm_resource_group.network_rg.name
  deployment_mode     = "Incremental"
  template_body       = <<DEPLOY
        {
          "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
          "contentVersion": "1.0.0.0",
          "resources": [],
          "outputs": {
              "uniquestring": {
                  "type": "string",
                  "value": "[uniquestring(resourceGroup().id, subscription().id)]"
              }
          }
        }
        DEPLOY
}

I can use the output from this in the name used in the Storage Account deployment.

1
2
3
4
5
6
7
resource "azurerm_storage_account" "diagnostics_storacc" {
  name                      = "stordiags${azurerm_template_deployment.uniquestring_arm.outputs["uniquestring"]}"
  location                  = azurerm_resource_group.network_rg.location
  resource_group_name       = azurerm_resource_group.network_rg.name
  account_tier              = "Standard"
  account_replication_type  = "LRS"
}

Hang on a sec, am I doing this right??

So, what I’ve described here probably isn’t “right” from a Terraform perspective.

I’m used to working with ARM deployment, that have no concept of state. I tend to deploy templates as layers, using ARM templates kind of like modules, but deployed separately. I’ll often use PowerShell to deploy the templates I want, and will pass outputs from one template as inputs to the next. This saves building specific nested templates for each customer solution - each having separate copies of largely identical templates.

Doing the same thing in Terraform likely isn’t going to work as Terraform knows what the current state should be. I don’t think it is going to play well if multiple separate Terraform deployments are trying to deploy/manage the same resources.

I still feel this useful as a learning exercise though, as much around how to approach Terraform configurations than the specifics around deploying ARM templates and getting their outputs using Terraform.

This post is licensed under CC BY 4.0 by the author.