Control Virtual Machines Sizes That Azure Admins Can Deploy

Microsoft-Azure-cloud-hero
In this post, I will show you how you can prevent delegated administrators from deploying unwanted & expensive large virtual machine sizes, which granting them access to deploy smaller machines.

Azure Resource Manager Policy

One of the dangers, and understandable fears, about cloud services such as Azure is that IT as a utility makes it possible for IT people to go wild. I’ve seen it personally in my training courses, where a typical course should use maybe $50 of credit, but an attendee goes nuts and runs up a bill for hundreds of dollars. There are a few things we can do to control this, including:

  • Monitoring bills: Every night, Azure produces data that allows you to see how much credit has been consumed. In CSP subscriptions, the CSP reseller has to retrieve and share this data. Note that this kind of data is not live — you only see what happened up to and including the previous day.
  • Credit caps: Depending on how you have purchased Azure, there are ways to cap credit on a per subscription basis.

But how do you enforce policies? Maybe you want to restrict people to certain kinds and sizes of virtual machines? For example, for a light workload, do people genuinely need to deploy D14 or GS5 virtual machines?
 

 
Azure Resource Manager (ARM) has a feature called ARM Policy; this can be used in a variety of ways, including:

  • Enforce the usage of certain kinds of fields, such as (billing) tags.
  • Use naming standards
  • Restrict access to certain kinds of resources, such as virtual machines or storage accounts.
  • Limit which sizes of virtual machines can be deployed

It’s that last option that is the focus of this post. We can create a JSON file that describes what specs & sizes of virtual machines that can be deployed, and we can assign that policy to an Azure subscription or to a resource group.

The JSON File

The method for taking control of a subscription or a resource group is based on Azure Resource Manager JSON. A JSON can be build to describe what a user can or cannot do, or even to enforce certain configurations or naming standards. In this example, we want to limit what virtual machines can be deployed in the rg-accounting resource group to:

  • Standard_A1
  • Standard_A2
  • Standard_D1_v2
  • Standard_F1

How did I get those naming standards? I ran an (Azure Resource Manager) PowerShell cmdlet specifying the Azure region that I want to work in:

Get-AzureRMVMSize -Location WestUS

Next we will build a JSON template using a tool such as the free VSCode. Open File > Preferences > User Settings, and enter the JSON schema details there.

{
    "$schema": "http://schema.management.azure.com/schemas/2015-10-01-preview/policyDefinition.json"
}

Defining the JSON schema in VSCode user settings [Image Credit: Aidan Finn]
Defining the JSON schema in VSCode user settings [Image Credit: Aidan Finn]
In VSCode, create a new file and type/paste in the below JSON. The file tells Azure:

  1. That it is working with virtual machines.
  2. If someone tries to create a virtual machine spec that it is not listed.
  3. Deny the request.
{
  "if": {
    "allOf": [
        {
        "field": "type",
        "equals": "Microsoft.Compute/virtualMachines"
        },
        {
        "not":
            {
            "field": "Microsoft.Compute/virtualMachines/sku.name",
            "in": ["Standard_A1","Standard_A2","Standard_D1_v2","Standard_F1"]
            }       
        }
    ]
  },
  "then":
    {
    "effect": "deny"
    }
}

Save the file as a JSON file type –I saved my file as C:\Temp\RestrictAzureVMs.json.

Define the Azure Policy

We need to upload the JSON file into Azure to define a new policy. Open a PowerShell prompt. Sign into Azure Resource Manager:

Login-AzureRMAccount

List your Azure subscriptions using the following:

Get-AzureRmSubscription

Note the ID of the Azure subscription that you want to work with, and select it. The subscription ID will be reused later, so I’m storing it in a variable:

$SubID = (Get-AzureRmSubscription -SubscriptionId 12345678-1234-1a34-abcd-a123bcd12345).SubscriptionId
Select-AzureRmSubscription -SubscriptionId $SubID

Now we can upload the JSON file to define a new ARM policy called RestrictVMSizes:

$Policy = New-AzureRmPolicyDefinition -Name RestrictVMSizes -Description “Policy to restrict VM sizes to predefined SKUs” -Policy C:\Temp\RestrictAzureVMs.json

You can view your new policy by running:

Get-AzureRmPolicyDefinition -Name RestrictVMSizes

Assign the Policy to a Resource Group

It is possible to delegate administrative rights to non-subscription administrators. You can create a resource group, add users to a group, and then assign that group rights to the resource group. When the users sign into the Azure Portal, all that they can see is the resource group and its contents, and all they can do is what you have allowed them to do. But they can do it at any scale, unless you assign an ARM policy definition to the resource group.
Assigning a policy is easy; first, you will need the unique identifier of the resource group that you want to assign the policy to. In my example, I am working with a resource group called rg-accounting:

$RGID = (Get-AzureRmResourceGroup -Name "rg-accounting").ResourceID

Now I can use the resource group ID ($RDID) to assign the previous policy definition ($Policy) to the resource group:

New-AzureRmPolicyAssignment -Name RestrictVMSizes -PolicyDefinition $Policy -Scope $RGID

Now if I try to create a virtual machine that is not listed in my policy (see the above JSON file), the process will fail (note that other resources such as storage accounts, NICs, etc. will be created first). The below screenshot is of such a failed deployment in the Azure Portal.

ARM Policy blocks the creation of the virtual machine [Image Credit: Aidan Finn]
ARM Policy blocks the creation of the virtual machine [Image Credit: Aidan Finn]

Assign the Policy to a Subscription

Larger businesses might have more than one Azure subscription, with the intention to assign subscriptions as a way to assign IT budgets. They may have a scenario, like I have with my training classes, where they need to control consumption at a subscription level instead of at a resource group level. We can assign an ARM policy to a subscription.

We already have the subscription ID ($SubID) from when we logged in to the subscription using Azure. Now we can assign the policy ($Policy) to the subscription using the following. Please note the use of /subscriptions/ before $SubID:

New-AzureRmPolicyAssignment -Name RestrictVMsizes -PolicyDefinition $Policy -Scope /subscriptions/$SubID

This policy now affects every administrator that tries to create virtual machines in the subscription and limits users to creating only machines that are listed in the policy.