Azure Resource Manager - Security policies

Azure Resource Manager provides an advanced template orchestration engine. By integrating RBAC you can define who gets access to what resources. RBAC can be configured at the subscription level, resource group level and on individual resource level. Many organizations have additional requirements when it comes to controlling possibilities at deployment time. You can think of allowing deployment only in certain region, enforcing a type of naming convention, denying a particular resource type to be deployed. This is where Security Policies come in. Security Policies are complementary to RBAC and combined they allow organization to utilize the platform in advanced ways still complying with their policy requirements and conventions.

Prerequisites

In this blog we will create and assign a security policy. To follow the steps in the blog you will need

  • Microsoft Azure subscription
  • Visual Studio with Microsoft Azure SDK
  • Azure PowerShell module

Visual Studio community edition can be installed directly from the Web Platform Installer, you can even select an installation with comes with the latest Microsoft Azure SDK . The Microsoft Azure PowerShell module can also be installed from the Web Platform Installer.

Security Policies

The configuration process of Security Policies consists of two parts. First we need to create a Policy Definition and then we assign that Policy Definition. Policies can be assigned at the subscription, resource group or resource level.

Example

We’ll start be creating and assigning an simple security policy. This allows you to understand the concept quickly, before diving in to the details. Each security policy that you create can be deleted and each security policy that you assign can be removed. You can safely create a Resource Group in an existing subscription, create and a assign a security policy to that Resource Group and delete it afterwards, without affecting any of the other Resource Groups or resources in the same subscription. In fact, this example does exactly that.

Start by signing in from you a PowerShell session

Login-AzureRmAccount

We’ll create a dedicated Resource Group for this example

$ResourceGroup = New-AzureRmResourceGroup -Name security.policy -Location "West Europe"

Next we will create the Policy Definition

$PolicyDefinition = New-AzureRmPolicyDefinition -Name myPolicyDefinition  -DisplayName myPolicyDefinition -Description "Do not allow the creation of Storage Accounts" -Policy "{""if"":{""source"":""action"",""equals"":""Microsoft.Storage/storageAccounts/write""},""then"":{""effect"":""deny""}}"

The last step is to assign the policy to the Resource Group we just created. To define the scope, you will need to get your subscriptionId. You can get your subscriptionId with the Get-AzureRmSubscription cmdlet.

$Subscription = Get-AzureRmSubscription -SubscriptionName "Developer Program Benefit"
$ResourceGroupName = $ResourceGroup.ResourceGroupName
$PolicyAssignment = New-AzureRmPolicyAssignment -Name myPolicyAssignment -PolicyDefinition $PolicyDefinition -Scope /subscriptions/$Subscription/resourceGroups/$ResourceGroupName

The policy we just assigned to the new Resource Group, denies the write action in the Microsoft.Storage/storageAccounts namespace. We can validate the Security Policy by trying to create a new storageAccount in the Resource Group.

$StorageAccountName = 'storage' + (get-random -Minimum 123456789 -Maximum 987654321)
New-AzureRmStorageAccount -ResourceGroupName $ResourceGroup.ResourceGroupName -Name $StorageAccountName -Type Standard_LRS -Location "West Europe"

This fails with the error

New-AzureRmStorageAccount : RequestDisallowedByPolicy: The resource action 'Microsoft.Storage/storageAccounts/write' is disallowed by one or more policies. Policy identifier(s): 
'/subscriptions/11430ac3-e443-4f27-bbac-82775ecd76c1/providers/Microsoft.Authorization/policyDefinitions/myPolicyDefinition'.

The security policy is enforced in Azure Resource Manager. No matter what tool you use to connect (Portal, PowerShell, VS, Cross Platform CLI or community tooling) the security policy applies to all of them. When we try to create a storageAccount in the Resource Group from the Portal, we are presented with an error as well.

{
  "error": {
    "code": "RequestDisallowedByPolicy",
    "message": "The resource action 'Microsoft.Storage/storageAccounts/write' is disallowed by one or more policies. Policy identifier(s): '/subscriptions/11430ac3-e443-4f27-bbac-82775ecd76c1/providers/Microsoft.Authorization/policyDefinitions/myPolicyDefinition'."
  }
}

Note: if you assign this security policy to an existing resource group with an existing storage account, it only denies the creation of new storage accounts in the that resource group, you can still make changes to the existing storage account.

To remove the resources, we used for this example run the following cmdlets. Please note that the last line removes the Resource Group. If you have used this example to an existing resource group that you do not want to delete, do not run the last cmdlet.

Remove-AzureRmPolicyAssignment -Id $PolicyAssignment.PolicyAssignmentId
Remove-AzureRmPolicyDefinition -Id $PolicyDefinition.PolicyDefinitionId
Remove-AzureRmResourceGroup -Name $ResourceGroup.ResourceGroupName

Policy Definition

Now that you have an idea of the capabilities of a security policy let’s look at the policy in more detail. A policy has a condition (when is the policy applied) and an effect (what is the action to take). The simplest example is

{
  "if" : {
    <condition> 
  },
  "then" : {
    "effect" : "deny"
  }
}

The effect can either be deny or audit.

The condition is configured with logical operators.

"if" : {
  "<logical operator>" : {
    "field" : "<field>",
    "condition" : ["<fieldvalue1>" , "<fieldvalue2>"]
  }
}

For example, this condition is true if the specified location does not match “northeurope” or “westeurope”

"if" : {
  "not" : {
    "field" : "location",
    "in" : ["northeurope" , "westeurope"]
  }
}

Logical Operators

There are three supported logical operators (Not, And, Or)

Operator Syntax
Not “not”
And “allOf”
Or “anyOf”

You can use nested operators for more advanced policy configuration.

"if" : {
    "not" : {
      "anyOf" : [
        {
          "source" : "action",
          "like" : "Microsoft.Resources/*"
        },
        {
          "source" : "action",
          "like" : "Microsoft.Compute/*"
        },
        {
          "source" : "action",
          "like" : "Microsoft.Storage/*"
        },
        {
          "source" : "action",
          "like" : "Microsoft.Network/*"
        }
      ]
    }

Conditions

A condition evaluates whether a field or source meets certain criteria.

Condition Syntax
Equals “equals” : “"
Like “like” : “"
Contains “contains” : “"
In “in” : [ “","" ]
ContainsKey “containsKey” : “"

Visual Studio

In the initial example we specified JSON for the policy directly in the PowerShell cmdlet to create a new Policy Definition.

$PolicyDefinition = New-AzureRmPolicyDefinition -Name myPolicyDefinition  -DisplayName myPolicyDefinition -Description "Do not allow the creation of Storage Accounts" -Policy "{""if"":{""source"":""action"",""equals"":""Microsoft.Storage/storageAccounts/write""},""then"":{""effect"":""deny""}}"

It is a quick way to specify the policy, but it is very hard to edit the actual policy this way. Not even to mention creating it from scratch. Luckily we can create and edit the policies in Visual Studio. There is even a schema that provides intellisense for editing the policies in Visual Studio

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

Open Visual Studio and create a new file, by selecting File > New > File from the menu. In the dialog box that opens type json in the searchbox on the right top.

Select to create a new JSON file. Next copy the schema link in the schema: box on the top of the file

Copy the following code between the two existing brackets

  "if": {
    "source": "action",
    "equals": "Microsoft.Storage/storageAccounts/write"
  },
  "then": { "effect": "deny" }

Save the file locally (e.g. c:\securityPolicies\myPolicy.json). You can create a new Policy Definition by referencing this file instead of specifying the policy inline.

$PolicyDefinition = New-AzureRmPolicyDefinition -Name myPolicyDefinition  -DisplayName myPolicyDefinition -Description "Do not allow the creation of Storage Accounts" -Policy c:\securityPolicies\myPolicy.json

BuiltIn policy definitions

Besides the policy you define, there are also a couple of BuiltIn policies. You can look at the details of these policy by running the following cmdlet

Get-AzureRmPolicyDefinition

There are 8 default policy definitions. The names are configured with a GUID. In the properties the PolicyType is set to BuiltIn.

  • Allow resource creation only in India data centers
  • Allow resource creation only in Japan data centers
  • Allow resource creation only in European data centers
  • Allow resource creation only in United States data centers
  • Allow resource creation if ‘environment’ tag value in allowed values
  • Allow resource creation only in Asia data centers
  • Allow resource creation if ‘department’ tag set
  • Allow resource creation only in Japan data centers

You can verify if and what policy has been assigned at what scope by running the following cmdlet.

Get-AzureRMPolicyAssignment

If the output doesn’t have any results, it means that there are currently no Policy Definitions assigned.

Create a Policy Definition

To create a new Policy Definition, you can use the New-AzureRmPolicyDefinition cmdlet. New-AzureRmPolicyDefinition -Name regionPolicyDefinition -Description "<description of the policy>" -Policy "<inline code or path to json file>" -DisplayName "<this name is displayed in the properties of the policy definition>"

With the example used earlier the actual cmdlet looks like

New-AzureRmPolicyDefinition -Name myPolicyDefinition -Description "Do not allow the creation of Storage Accounts" -Policy "c:\securityPolicies\myPolicy.json" -DisplayName myPolicyDefinition

Please note: Creating a new PolicyDefinition with New-AzureRmPolicyDefinition with the same name as an existing PolicyDefinition, overwrites the existing Policy Definition. Even if the policy definition has been assigned already! Please be careful with the name you specify for a new Policy Defintion. Overwriting an existing assigned policy can have an unwanted impact on your tenant’s capabilities.

If you look at the Properties closely you can see that the PolicyRule property is empty. This might be confusing. The Policy seems empty, but if you create a variable with the Policy Definition you can retrieve the PolicyRule

$PolicyDefinition = Get-AzureRmPolicyDefinition -Name "myPolicyDefinition"
$PolicyDefinition.Properties.PolicyRule

Assign Policy Definition

Before the security policy becomes active we need to assign the Policy Definition. It is possible to assign a Policy Definition at different levels.

Assigned to Applies to Scope
Subscription All resource groups and resources in the subscription /subscriptions/{subscription-id}/
Resource Group All resources in that Resource Group /subscriptions/{subscription-id}/resourceGroups/{resourceGroupName}
Resource Only to that resource /subscriptions/{subscription-id}/resourceGroups/{resourceGroupName}/providers/{namespace}/{ResourceType}/{ResourceName}

First define a variable that holds the PolicyDefinition you want to assign.

$PolicyDefinition = Get-AzureRmPolicyDefinition -Name myPolicyDefinition

Next we can assign the policy at the subscription level

$Subscription = Get-AzureRmSubscription -SubscriptionName "<Name of your subscription>"
New-AzureRmPolicyAssignment -Name myPolicyAssignment -PolicyDefinition $PolicyDefinition -Scope /subscriptions/$Subscription/

If you look at the properties in the output or with Get-AzureRmPolicyAssignment, the scope of the assignment displayed.

You can assign the policy at the resource group level

$Subscription = Get-AzureRmSubscription -SubscriptionName "<Name of your subscription>"
$ResourceGroupName = "<Name of your resource group>"
New-AzureRmPolicyAssignment -Name myPolicyAssignment -PolicyDefinition $PolicyDefinition -Scope /subscriptions/$Subscription/resourceGroups/$ResourceGroupName

It is also possible to assign a Policy to an individual resource. The cmdlet for this requires the details of the resource. The easiest way to get these is get all resource in your subscription.

Get-AzureRmResource | Sort-Object ResourceType | Ft ResourceType, ResourceName, ResourceGroupName, Location, ResourceId -Wrap

Copy the complete ResourceId of the resource you want to Policy assigned to.

$Resource = Get-AzureRmResource -ResourceId "<paste the ResourceId taken from the previous cmdlet>"
New-AzureRmPolicyAssignment -Name myPolicyAssignment -PolicyDefinition $PolicyDefinition -Scope $Resource.ResourceId

If you have performed the steps exactly as outlined in this blog, you will notice that if you run the Get-AzureRmPolicyAssignment there are three assignments with the same Policy Definition, assigned at different scopes. And based on the scope each assignment has its own properties.

Remove security policy

Since the assignment and the policy definition are two different entities, you can remove an assignment without removing the Policy Definition. If you closely look at the previous picture, you will notice that all assignments have the same name. When you perform a Remove-AzureRmPolicyAssignment you will be prompted to specify the Scope. The scope is specified within the properties field, that also contains different values. You can also remove a policy assignment by specifying the PolicyAssignmentId.

Remove-AzureRmPolicyAssignment -Id <copied from the Get-AzureRmPolicyAssignmentId in the Get-AzureRmPolicyAssignment output>

For example, if I wanted to remove the Policy Assignment at the subscription level.

Logging

After you have assigned one or multiple Policies, event will be logged for each effect. Events with a deny effect are logged with the status Forbidden, events with an audit effect are logged with the action. You can view these events in the Portal or retrieve them through PowerShell with Get-AzureRmLog

More information

Security Policies really complement Role Based access control Azure Resource Manager. Opening more scenarios for organizations that have these kinds of requirements. Make Microsoft Azure an even more enterprise-grade cloud computing platform.

https://azure.microsoft.com/en-us/documentation/articles/resource-manager-policy/