Proper Azure policy to verify Azure hybrid benefit enabled

Azure policies allows Azure admins to enforce or verify how Azure resources are deployed in environment. It relies on Azure Policy definition file written in JSON which stipulates what condition resource shall adhere to pass or fail a policy and what effect it will have on resource (deny or audit)

This is very good way to prevent certain things from happening before it happens (like deploying resources in unapproved locations etc) which are not possible to accomplish with plain RBAC controls.

For this specific case I needed to ensure that all VMs in subscription are enabled with Azure Hybrid Benefit which saves up to 40% of Windows licensing costs if company already has EA agreement with Microsoft and will not pay double licensing costs for OS.

Searching Microsoft samples actually yield a result which worked fine for sometime untill I was alerted that some machines are still passing a test despite the fact that they are not using Hybrid Benefit.

Looking at policy it becomes apparent what the issue is. Definition of original policy is below and applicable only to images created from Azure gallery Windows images. So if you created VM through ASR or created VM from custom image this policy will not apply. Another issue is that policy does not apply to Windows client machine if you happen to have those in your environment since license types of those is named differently.

 "if": {
                "allOf": [
                    {
                        "field": "type",
                        "in": [
                            "Microsoft.Compute/virtualMachines",
                            "Microsoft.Compute/VirtualMachineScaleSets"
                        ]
                    },
                    {
                        "field": "Microsoft.Compute/imagePublisher",
                        "equals": "MicrosoftWindowsServer"
                    },
                      {
                        "field": "Microsoft.Compute/imageOffer",
                        "equals": "WindowsServer"
                    },
                    {
                        "field": "Microsoft.Compute/imageSKU",
                        "in": [
                            "2008-R2-SP1",
                            "2008-R2-SP1-smalldisk",
                            "2012-Datacenter",
                            "2012-Datacenter-smalldisk",
                            "2012-R2-Datacenter",
                            "2012-R2-Datacenter-smalldisk",
                            "2016-Datacenter",
                            "2016-Datacenter-Server-Core",
                            "2016-Datacenter-Server-Core-smalldisk",
                            "2016-Datacenter-smalldisk",
                            "2016-Datacenter-with-Containers",
                            "2016-Datacenter-with-RDSH"
                        ]
                    },
                    {
                        "field": "Microsoft.Compute/licenseType",
                        "notEquals": "Windows_Server"
                    }
                ]
            },

To find our what all aliases are used for specific resource you can execute following powershell statement (Get-AzPolicyAlias -NamespaceMatch 'Microsoft.Compute').Aliases. It will list all aliases available to be using in Microsoft.Compute resource provider. To identify Windows only boxes we can narrow down the search to osType property

PS Azure:\> (Get-AzPolicyAlias -NamespaceMatch 'Microsoft.Compute').Aliases  | where Name -match ostype | select Name

Name
----
Microsoft.Compute/virtualMachines/storageProfile.osDisk.osType
Microsoft.Compute/virtualMachineScaleSets/virtualMachineProfile.storageProfile.osDisk.osType
Microsoft.Compute/virtualMachineScaleSets/virtualMachines/storageProfile.osDisk.osType
Microsoft.Compute/galleries/images/osType
Microsoft.Compute/disks/osType
Microsoft.Compute/snapshots/osType
Microsoft.Compute/images/storageProfile.osDisk.osType

From the list you can clearly see that Microsoft.Compute/virtualMachines/storageProfile.osDisk.osType is properly alias to use in matching. You can verify aliases on existing VMs you can use Azure Resource Graph in preview portal by executing following query

where type=~'Microsoft.Compute/virtualMachines'
| where name =~ 'pr7-material'
| project aliases

So resulting Azure Policy Definition rule shall look like below which will identify all Windows VMs regardless how they were created in your subscription and ensure hybrid benefit is enabled on them.

   "policyRule": {
      "if": {
        "anyOf": [
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Compute/virtualMachines"
              },
              {
                "field": "Microsoft.Compute/imagePublisher",
                "in": [
                  "MicrosoftWindowsServer",
                  "MicrosoftWindowsClient"
                ]
              },
              {
                "allOf": [
                  {
                    "field": "Microsoft.Compute/licenseType",
                    "notEquals": "Windows_Server"
                  },
                  {
                    "field": "Microsoft.Compute/licenseType",
                    "notEquals": "Windows_Client"
                  }
                ]
              }
            ]
          },
          {
            "allOf": [
              {
                "field": "type",
                "equals": "Microsoft.Compute/virtualMachines"
              },
              {
                "field": "Microsoft.Compute/virtualMachines/storageProfile.osDisk.osType",
                "equals": "Windows"
              },
              {
                "allOf": [
                  {
                    "field": "Microsoft.Compute/licenseType",
                    "notEquals": "Windows_Server"
                  },
                  {
                    "field": "Microsoft.Compute/licenseType",
                    "notEquals": "Windows_Client"
                  }
                ]
              }
            ]
          }
        ]
      },

Below result catching windows box created not from gallery failing audit as a result

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s