Engineering Blog

Azure and our approach to naming things

By Paul on December 17, 2016

Azure currently has some pretty interesting rules and restrictions around how things can be named. At times they seem arbitrary, but none-the-less, it’s what we have to work with.

What follows is a copy and paste from our internal wiki, explaining our strategy to naming in Azure. We’ve posted them here so that, hopefully, someone else finds it useful and as per our values we’re all about being open. It’s a bit dry, but the topic at hand isn’t one to inspire literary genius.

A note before you begin: we have some distinct, but not unique requirements. Our intent is to deliver our product to customers in such a way as to allow them to host the application in their own subscription. This makes for some choices in our conventions, that might not otherwise be required if you don’t plan to follow a similar pattern of deployment. Additionally, I destroy, and spin up entire environments almost constantly, thus the requirement to make each storage account name unique – deletion of storage accounts takes time; they are processed on a queue, and aren’t always completely dead when the resource deletion has confirmed completion. Take what is useful, and dump what it not.

And so here we have it:

The FINBOURNE Azure naming conventions

Suffice to say this is a work in progress, and subject to change based on feedback refs:

Following the guidelines, and in keeping with our own desire to be able to deploy to any customer subscription, the following structure will be employed.

  • Identify the following items:
    • Company name
    • Environment
    • Location
    • Azure Subscription

This should meet our very own structure, where each engineer can have their own Azure environment, plus multiple CI/CD environments that form the pipeline through to the publicly facing application.

Some of these things can of course be defaulted – for instance, Location can default to UKSouth, Environment can be set to a reproducible munged username string and so on. Defaults should reflect the most common case, obviously allowing overriding.

Publicly facing names point at the convention based name for the component.

Prefix

Given we need something that is human readable, and identifiable as originating from us, we should prefix all resources with a short name for FINBOURNE – as short as possible – let’s say fb.

Why do we want to do this? Because from experience it’s incredibly useful and an easy win to cluster all of our resources together

Naming strategy

Our naming strategy is such: resource components separated by hyphens. Strip non-alphanumeric characters in component name. All in lower case: fb-companyname-location-environment-servicename Location is not really required, since it is encoded elsewhere, however since certain other resources can only co-exist with resources that exist in the same location, it’s been added to make identification convenient. Note that we are getting more granular from left to right. A diagram could easily be constructed that had concentric rectangles that represented this naming convention, and making obvious sense. Lowercase is important as some of the resource names will be converted into URL’s, which on some OS’s is case sensitive. Keeping things lowercase reduces issues around url casing, and the acceptability of said urls. For example:To deploy the security-api to a DEV environment for ACME in UK-South, we would get the following resource name:

fb-acme-uks-dev-securityapi

Azure Global Resources

From the linked page, there are 3 global resources, 2 of which can be considered the same from the point of view of naming. Leaving us with 2 + public domain resource naming.

  • Resource Group
  • Storage
  • Public Name (attached to public ip addresses)

From Resource Manager Template Best Practices, some common resources that require unique names include:

  • Storage accounts
  • Web sites
  • SQL servers
  • Key vaults
  • Redis caches
  • Batch accounts
  • Traffic managers
  • Search services
  • HDInsight clusters

Public names (i.e. a domain name) can be up to 255 characters long. Azure TLD’s take up around 40 of these characters, leaving a little over 200 characters for the created sub domain. Azure however, only allows for 63 of these characters to be used for public IP’s, and even then, only with alphanumeric+hyphen. This has an impact on some of the naming in other places.

Encoding Resource Group Names

  • We have 64 characters to play with for resource groups.
  • We are allowed alphanumeric + underscore and hyphens.
  • If we reserve hyphens for splitting, then remove all other characters outside the alphanumeric range in component names.
  • Do not use underscores _

Following this naming strategy is in keeping with that mentioned earlier under the Naming Strategy heading. We use nothing except alphanumeric and hyphens, and hyphens are only used for splitting, to aid readability.

Encoding Storage Names

The most restrictive length for a global resource name is 24 characters – Storage Accounts. Some part of this needs to be identifiable as belonging to us. So, we prefix each storage account with the short name for FINBOURNE. This leaves a further 21 characters to define the storage account.

uniqueString()[4] – The ARM templating function, can produce a 13 character hash based on variable length inputs. Using: uniqueString() with the following input uniqueString(customername, location, environment, service, storagename)[1], gives us a reasonably simple and repeatable way of producing a unique resource name.

Additionally, suffix that string with a 3 character monotonically incrementing number, that represents the current deployment (+1 over the last deployment) to prevent storage deletion issues around the re-creation of the storage accounts. To reduce the proliferation of “config”, one deployment key should be kept. Deployments should increment the value of this key by 1 – modulo 1000, regardless of whether it’s a full deploy, partial deploy, deployed to localhost, deployed to DEV or deployed to Production. Our tooling should seamlessly enable this.

We then have the following resource name for a storage account based on customer name = ACME, service name = web, environment = dev, location = australiasoutheast (australiasoutheast is the longest name for an Azure location as at the time of writing), and storage name of apiagent1 uniqueString(“acme”, “australiasoutheast”, “qa”, “web”, “apiagent1”) = fbbcx3dmne3luqd045[2]

There is every chance that we could get away with an un-obfuscated version but it would be so terse it would almost look the same. It would also make edge cases, and conflicts hard to redefine. This method should be relatively future proof and extensible if need be. If in future, the restrictions on these names are redefined, we can re-visit this logic to ensure it is still suitable.

Encoding Public Names

Public names can be up to 63 characters long. Follow the same rules as for resource group naming.

Naming conflicts

It is unlikely that if we follow these rules for global naming that we will run into any naming conflicts with other Azure users. However, the chance still exists, so we need a strategy for coping with such conflicts. This needs to fit in with our other tooling for deployments – see the section on tooling.

To this end, overrides need to be baked into the naming strategy. Given we have quite a bit of space available to us with public names, should a naming conflict occur, we can add a 13 character – using uniqueString()[4] – base 36 encoded hash of the already generated public name to the end (with hyphen separation). If for some reason this fails, follow the rules for conflict on a storage name – below.

Conflicts on storage names:

Should this occur at any time, and it does seem unlikely given the explained strategy, then adding a three-digit number to the end of the resource name input to the generator should produce a sufficiently distinct name. I would hope that a three-digit prime would work. The largest three-digit prime, whose digits are also prime is 773, so let’s use that as a starting point. Given the previous example of uniqueString(“acme”, “australiasoutheast”, “qa”, “web”, “apiagent1”), we would then append 773, and get uniqueString(“acme”, “australiasoutheast”, “qa”, “web”, “apiagent1”, “773”), giving fb3nwfsazjsf0zx045. As you can see, significantly different. Should this also fail, work upward in primes from 773[3].

The likelihood of having to do this is negligible, but it wouldn’t be the first time in computing history that the negligible became not only probable, but also highly likely. For the same reasons that we are attempting to understand how to name things in a consistent way in Azure, other individuals and companies are also engaging in the same thought process, it’s not outside the realms of possibility that 2 or more groups collide for one reason or another.

Non-Global Naming

When it comes to non-global naming, we have significantly fewer restrictions in terms of length, and also naming convention, due to the scope of the resource. This means we can drop some of the prefixes, because that information is already ascertainable via the owning scope. Of course, make it meaningful to the component/application it is related to, but other than that, be sensible. As a guideline, prefix with fb- where possible, include the service name and any other relevant information – hyphenated and lowercase.

In this category, VM names get a special mention. They are only allowed to be 15 characters long. This poses a special challenge, and we definitely must rely on its owning resource (the resource group) to supply us with information as to its other ownership – company, environment, location, application. VM names should still be as meaningful as possible, so identifying what they do is crucial. With 15 characters we probably just want to identify that it belongs to FINBOURNE, what application it’s running, and possibly what number resource it is. At this point, the simplest example I can think of is TeamCity. Naming VM’s would go something like: fb-tc-ag-01, fb-tc-ag-02 etc for agents and fb-tc-srv-01 for the server.

Given the prefix and suffix of fb- and -01 that leaves only 8 characters to describe what the VM is up to. Abbreviation is paramount. Be creative, descriptive, explicit and sensible.

Tooling

As a defined practice, we will be creating Azure ARM Templates to describe our deployments for each component. Additionally, to this, we will wrap deployment in a Rake task. This task is responsible for defining, retrieving and prompting for both regular and secure parameters. These parameters are output to a file .parameters.json[5] and that filename should be passed as a parameter into the Azure cli group deploy command.

If for some reason, some transformation of the template is required before being run, then use the following substitution opening and closing strings for the transformation: opening – [%= : closing – %]. This is sufficiently different from the template formats that it should be easily recognised visually, as well as making it simple to parse with REGEX or string substitution. The output filename should, like the parameters file be output to a temporary directory, and prefixed with a period. Also consider suffixing it with a unique but reproducible string for the deployment. This file should also be cleaned after a successful deployment, and retained if debug is requested, or after failure.

  • [1] storagename is used to uniquely identify each storage account within a resource. Imagine a TeamCity resource group with multiple agents, each needing their own VM, and possibly independent storage account
  • [2] note the fb prefix, and the 3 number suffix indicating that this is deployment number 045
  • [3] in ascending order 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997
  • [4] see http://www.codeisahighway.com/use-uniquestring-function-to-generate-unique-names-for-resources-in-arm-template/
  • [5] why the leading period – .? we can git ignore anything starting with a period. This just helps prevent committing passwords to a repo by accident, even if the rest of the filename were typoed, we’d be safe from that particular blunder. Preferably, we export the parameters to a temp directory, with a trailing unique (but reproducible) string for the deployment, so that it looked something more like .parameters.3as3kii9.json