Problem:

Usually when an organization is moving to the cloud, their staff hasn’t mastered Infrastructure as Code and rely on the Azure Portal to deploy their resources. This can be problematic if you are not enforcing your standards with Azure Policy. Resources will be deployed with the wrong name, whether intentional or not, and they will need to be deleted and redeployed to fix the bad name. In my solution, I will show how you can enforce your naming convention for your Azure virtual machines and hopefully prevent you and your organization from having to clean up your Azure subscription.

Solution:

  1. Open my Github repo
  2. Click on the “Deploy to Azure” or “Deploy to Azure Gov” button
  3. Login to Azure
  4. Save the Policy definition to appropriate scope, subscription or higher
  5. Assign the Policy definition to a resource group

Explanation:

In the steps below, I will explain the code required to control the naming convention for your Azure Virtual Machines. Before we get to the code, let me elaborate on the limits of matching a string with Azure Policy. Regular expressions are currently NOT supported. Using the “match” or “notmatch” conditions, we have the following supported options:

  • “#” to match a digit
  • “?” to match a letter
  • “*” to match any character

With these options, its not easy to control the name at scale. To work around these limitations, we also need to use parameters and a lower level scope to get granular control over the naming convention. In my example, using virtual machines, it is assumed your central IT department or operations team will provision the resource groups for your application teams. During the provisioning, the IT or Ops team would also assign this policy to ensure the application team is abiding by the naming standard. Now on to the code.

Parameters block:

"Application": {
    "type": "String",
    "metadata": {
        "description": "Use 1 - 6 characters for the application name or abbreviation",
        "displayName": "Application Name or Abbreviation"
    }
},
"Environment": {
    "type": "String",
    "metadata": {
        "description": "Use 1 character for the environment abbreviation: 'd' = Dev, 'p' = Production, 's' = Stage, & 't' = Test ",
        "displayName": "Environment Abbreviation"
    },
    "allowedValues": [
        "d",
        "p",
        "s",
        "t"
    ]
},
"DataCenterCountry": {
    "type": "String",
    "metadata": {
        "description": "Use 2 characters to abbreviate the Azure country (e.g., 'us' for United States or 'ca' for Canada).",
        "displayName": "Data Center Country Abbreviation"
    },
    "defaultValue": "us",
    "allowedValues": [
        "ae",
        "at",
        "au",
        "br",
        "ca",
        "ch",
        "cl",
        "cn",
        "de",
        "dk",
        "es",
        "fr",
        "gb",
        "gr",
        "hk",
        "ie",
        "il",
        "in",
        "it",
        "jp",
        "kr",
        "mx",
        "nl",
        "no",
        "nz",
        "pl",
        "qa",
        "se",
        "sg",
        "tw",
        "us",
        "za"
    ]
},
"DataCenterLocation": {
    "type": "String",
    "metadata": {
        "description": "Use 1 - 2 characters to abbreviate the Azure location (e.g., 'e' for East US or 'e2' for East US 2).",
        "displayName": "Data Center Location Abbreviation"
    },
    "allowedValues": [
        "e",
        "e2",
        "e3",
        "c",
        "c2",
        "dc",
        "de",
        "ga",
        "gt",
        "gv",
        "n",
        "n2",
        "nc",
        "ne",
        "s",
        "sc",
        "se",
        "w",
        "w2",
        "w3",
        "wc"
    ]
},
"WebTier": {
    "type": "String",
    "metadata": {
        "description": "Use 1 character to abbreviate the web or presentation tier: 'p' = Presentation & 'w' = Web",
        "displayName": "Web Tier Abbreviation"
    },
    "defaultValue": "w",
    "allowedValues": [
        "w",
        "p"
    ]
},
"LogicTier": {
    "type": "String",
    "metadata": {
        "description": "Use 1 character to abbreviate the business logic tier:  'l' = Logic.",
        "displayName": "Logic Tier Abbreviation"
    },
    "defaultValue": "l",
    "allowedValues": [
        "l"
    ]
},
"DataTier": {
    "type": "String",
    "metadata": {
        "description": "Use 1 character to abbreviate the database tier: 'd' = Database.",
        "displayName": "Data Tier Abbreviation"
    },
    "defaultValue": "d",
    "allowedValues": [
        "d"
    ]
}

The following list highlights the reasoning behind each of these parameters:

  • Application: each resource group will only be used for one application per location. Microsoft’s best practices dictate that resource groups should contain resources with the same lifecycle. For high availability and BCDR, the application’s resources should be deployed in a unique resource group per location. For example, virtual machines that reside in East US should belong to a resource group in East US. The HA / BCDR virtual machines for the same application should be deployed to a second location and reside in a resource group that matches the second location, like West US. This will ensure you can manage your application in Azure in the event of a disaster.
  • Environment: this is an obvious parameter since you’ll want to specify the environment on all your Azure resources.
  • Data Center Country: initially this parameter wasn’t in my code. However, after recently helping a customer deploy WVD to multiple worldwide locations, I thought it was important for some organizations to include this in their naming convention if they have a worldwide footprint. There is one concern with using country codes though. Not all locations are listed by country. For instance, some locations are listed by continent, like Europe. To solve this conundrum, I broke out the names by the country where the datacenter actually resides. For example, the North Europe data center is located in Ireland so the country code for this location would be “IE”.
  • Data Center Location: this parameter is a simplified version of the location, using the first character in either the one term or two locations. Many locations are identified simply by north, south, east, and west. However, I included abbreviations for all the two term regions, like south central and east 2. For the US Gov regions, I simplified those down to “government” and the “state”. For example, US Gov Virginia is “gv” for the location.
  • Tiers (Web, Logic, Database): this policy assumes your app is an N tier application. If you are using a different architecture, these parameters can be modified to suit your needs.

Policy Rules block:

"if": {
    "allOf": [
        {
            "field": "name",
            "notMatch": "[concat('vm', parameters('Application'), parameters('DataCenterCountry'), parameters('DataCenterLocation'), parameters('Environment'), parameters('WebTier'), '#')]"
        },
        {
            "field": "name",
            "notMatch": "[concat('vm', parameters('Application'), parameters('DataCenterCountry'), parameters('DataCenterLocation'), parameters('Environment'), parameters('LogicTier'), '#')]"
        },
        {
            "field": "name",
            "notMatch": "[concat('vm', parameters('Application'), parameters('DataCenterCountry'), parameters('DataCenterLocation'), parameters('Environment'), parameters('DataTier'), '#')]"
        },
        {
            "field": "type",
            "equals": "Microsoft.Compute/virtualMachines"
        }
    ]
},
"then": {
    "effect": "deny"
}

In the Policy Rules block, the “allOf” operator is validating the virtual machine name. If the name of the virtual machine doesn’t match any of the conditions, it is denied. At the end of each name for the “notMatch” condition, there is an allowed ordinal or number. Only one digit is allowed, 0 through 9. If more than 10 servers are needed per tier, you would need to steal a character from another element in the naming convention, most likely the application name or abbreviation, and add a second “#” at the end of each name.

That’s essentially it for the code. The full description for the policy definition is in the policy.json file and will be displayed if you deploy this code through the Portal (referenced above in the Solution section). While this specific scenario focuses on virtual machines, this code can be used as a template to jumpstart your governance of other Azure resource types. Simply change the resource type in the policy rule section and modify the character conditions based on the allowed characters for that resource. Good luck!