Play with Tag on Azure Subscriptions

Tagging is often part of your cloud governance. You’d use tag to manage cost per subscription. You’d use it to distinguish environment type (e.g. non-production, production, test, development…)

In this article, let’s play with tag a bit. You will learn on how to get subscription tag via Azure ARM template or how to perform applying tag to all subscriptions.

Use Case

Tag is often heavily used to govern a large environment with a complex hierarchy. In a DevSecOps context you would want to get environment tag to deploy your resources to a right place (e.g. retrieving secret from regional Azure Key Vault for an application…)

Apply Tag

To test Azure ARM template, first we need to apply tag. Use the sample script below to apply tags to all subscriptions:

$globalTag = @{
    "Project" = "AzSec"
    "Year" = "2022"
    "Cloud" = "Azure"
}

# Only get active subscriptions
$subscriptions = Get-AzSubscription | Where-Object {$_.State -eq "Enabled" }
foreach ($subscription in $subscriptions) {
    Write-Host -ForegroundColor Green "[+] Found subscription named: " $subscription.Name
    Write-Host -ForegroundColor Green "`t[+] Start applying tag for subscription named: " $subscription.Name
    $tag = New-AzTag -ResourceId "/subscriptions/$($subscription.id)" -Tag $globalTag
    if ($tag) {
        Write-Host -ForegroundColor Green "`t[+] Succesfully applied tag for subscription named: " $subscription.Name
    }
    else {
        Write-Host -ForegroundColor Red "`t[!] Failed to applied tag for subscription named: " $subscription.Name
    }
}

ARM Template

Now let’s see how to add tags to a subscription:

{
  "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
  },
  "resources": [
    {
      "type": "Microsoft.Resources/tags",
      "apiVersion": "2021-04-01",
      "name": "default",
      "properties": {
          "tags": {
              "Cloud": "azure",
              "Year": "2022",
              "Project": "AzSec"
          }
      }
    }
  ]
}

The template above can be deployed to a subscription scope with New-AzSubscriptionDeployment. Be mindful that the existing tags may be overwritten.

Now let’s see how to get these tags using ARM template

{
  "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "subscriptionId": {
      "type": "string",
      "defaultValue": "[subscription().subscriptionId]"
    }
  },
  "variables": {
    "tagResourceId": "[concat('/subscriptions/',parameters('subscriptionId'),'/providers/Microsoft.Resources/tags/default')]"
  },
  "resources": [],
  "outputs": {
    "cloud": {
      "type": "string",
      "value": "[reference(variables('tagResourceId'),'2020-01-01').tags.Cloud]"
    },
    "year": {
      "type": "string",
      "value": "[reference(variables('tagResourceId'),'2020-01-01').tags.Year]"
    },
    "project": {
      "type": "string",
      "value": "[reference(variables('tagResourceId'),'2020-01-01').tags.project]"
    }
  }
}

Deploy the template and verify the output:

With this support, when working with a large complex ARM template you could make this as a nested template and get the deployment output for another dependencies.

Azure Policy

You can use Azure Policy to audit and deploy tag. Below is the sample policy in form of Azure ARM Template to help audit subscription tag.

{
  "$schema": "https://schema.management.azure.com/schemas/2019-08-01/managementGroupDeploymentTemplate.json#",
  "contentVersion": "1.0.0.0",
  "parameters": {
    "name": {
      "type": "string",
      "metadata": {
        "description": "The name of the policy"
      }
    },
    "displayName": {
      "type": "string",
      "metadata": {
        "description": "The display name of the policy"
      }
    },
    "description": {
      "type": "string",
      "metadata": {
        "description": "The description of the policy"
      }
    },
    "allowedEnvironmentTagValue": {
      "type": "array",
      "allowedValues": [
        "Production",
        "Development",
        "Test",
        "Staging"
      ],
      "defaultValue": [
        "Production",
        "Development",
        "Test",
        "Staging"
      ],
      "metadata": {
        "displayName": "The tag value of the environment tag for the subscription",
        "description": "The tag value of the environment tag for the subscription"
      }
    },
    "allowedSubscriptionCode": {
      "type": "array",
      "allowedValues": [
        "AzSecA",
        "AzSecB"
      ],
      "defaultValue": [
        "AzSecA",
        "AzSecB"
      ],
      "metadata": {
        "displayName": "The tag value of the environment tag for the subscription",
        "description": "The tag value of the environment tag for the subscription"
      }
    }
  },
  "resources": [
    {
      "type": "Microsoft.Authorization/policyDefinitions",
      "apiVersion": "2020-03-01",
      "name": "[parameters('name')]",
      "properties": {
        "policyType": "Custom",
        "displayName": "[parameters('displayName')]",
        "description": "[parameters('description')]",
        "mode": "Indexed",
        "parameters": {
          "policyEffect": {
            "type": "string",
            "allowedValues": [ "audit", "deny" ]
          },
          "metadata": {
            "displayName": "The policy effect mode of the subscription tagging Policy",
            "description": "The policy effect mode of the subscription tagging Policy"
          }
        },
        "policyRule": {
          "if": {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Resources/subscriptions"
              },
              {
                "not": {
                  "allOf": [
                    {
                      "field": "tags['Environment']",
                      "in": "[parameters('allowedEnvironmentTagValue')]"
                    },
                    {
                      "field": "tags['ApplicationTier']",
                      "in": "[parameters('allowedSubscriptionCode')]"
                    }
                  ]
                }
              }
            ]
          },
          "then": {
            "effect": "[[parameters('policyEffect')]"
          }
        }
      }
    }
  ],
  "outputs": {
    "resourceId": {
      "type": "string",
      "value": "[resourceId('Microsoft.Authorization/policyDefinitions', parameters('name'))]"
    }
  }
}

Resource Graph Explorer

You can query subscription tag by using the query below:

resourcecontainers
| where type == "microsoft.resources/subscriptions"
| project name, id, tags

What else would you need to know or would like to feedback please drop a comment in Comment section. Your feedback would be really appreciated.

You can learn more about Azure tagging strategy here.

This entry was posted in Governance & Compliance and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published.