Creating Azure Policies

Rogier Dijkman
8 min readDec 27, 2022

--

Ever needed to create an Azure Policy and had absolutely no clue where to start? We all have been there, and that is why I decided to create an Azure Policy 101 blog post.

In this blog I will explain the approach that I take when creating a policy definition from scratch. I won’t go in to every nitty gritty details of a policy, but just enough to understand the concept and become an Azure Policy warlock, beard not included…

Understand your resource

The first step in creating an Azure policy is determining the options that can be assessed by an Azure Policy. Simple said, every resource setting that you change in portal can be assessed by a policy definition. There are also settings that can be assessed which are not visible in the portal, but for now that is out the scope of this article.

Most of the blogs that I’ve read about creating Azure policies use an example to support only https traffic to a storage account.

Although this is a useful example, it didn’t help me in my journey to create more advanced policies. Therefor in this article we will take it one step further and try a more complex use case.

In this example we will create an Azure policy definition that will only allow the consumption based SKU when creating a Logic App resource. For this we first need to determine the available properties, which can already be a challenge for many people.

ResourceGraph Module

I will use the Microsoft Resource Graph PowerShell module to query the available properties. There are several other methods to do this, but using the PowerShell module is the most straight forward. If you are more experienced or want to bring your coding skills to the next level, you can also use the Microsoft Resource Graph to query the resource properties.

Installing the AZ.Resourcegraph module is easy and can be done by executing the following two lines of code:

Install-Module AZ.ResourceGraph -AllowClobber -Repository PSGallery
Import-Module Az.ResourceGraph -Force

In some cases you don’t have the permissions to install any PowerShell modules on your computer due to company policies or other restrictions.
In that case you can use the Invoke-RestMethod cmdLet to query the resource graph. This requires an access token. I have created a PowerShell script to get a valid access token which can be found on my [here]

Exploring properties

As said before, we are going for a more complex scenario where we want to limit the user in only using the consumption based logic apps. This is where it starts to become clearer why it is important to understand your resources.

A Logic App can be deployed in two different plan types, Consumption based and Standard. When choosing for the standard plan an App Service plan is created that will host one or more logic app workflows. The prices for an App Service plan vary between EUR 150,- p/month for a WS1 tier up to about 615,- p/month for the WS3 tier.

So what we need to do to prevent this and limit the type of App Service plans that can be deployed to consumption based.

Fire up your PowerShell and and connect to your Azure tenant.
Once you are logged in we can start to use the newly installed module to query the properties of our resource.

Because we want to find the property for the App Service Plan we are going to query the Microsoft.Web/serverfarms resource.
The limit 1 property returns only one resource instead of all App Service plans that are available in the target tenant.
We will output the result as a json object using the ConvertTo-Json function.

$result = Search-AzGraph -Query `
"Resources | where type=~'Microsoft.Web/serverfarms' | limit 1" | ConvertTo-Json

The result will show the properties of the AppService plan as shown in the image. The part that we are most interested in is the SKU values for our policy definition.

"sku": {
"name": "WS1",
"tier": "WorkflowStandard",
"capacity": 1,
"family": "WS",
"size": "WS1"
},

As we can see here is that the selected tier for the AppService plan is workflowStandard. This is the information we were looking for to use as validation value in our policy definition. It might look more logical to use the name property but that would impact the deployment of all App Service plans whereas when using the tier name we can only limit the App Service plan for Logic Apps.

The Policy Definition

How to create a policy definition is something that is well documented on the Microsoft Learn page. Therefor I will stick to a summary of commonly used options that I will use in this example.

More in depth information about authoring policies can be found using the links below:
Policy Definition JSON Schema

A policy definition contains elements for:
- displayName & description
- modes
- parameters
- policy rule
- effect

Mode

Mode determines which resource types will be evaluated for a policy. The supported modes are:

- all evaluate resource groups and all resource types
- indexed: only evaluate resource types that support tags and location

To improve the performance of the policy definition I generally choose the Indexed mode. By default if a policy definition is created in the Portal, the mode is set to all .

{
"mode": "Indexed"
}

Parameters

Parameters can help to simplify the policy management by reducing the number of policy definitions. By including parameters in a policy definition, you can reuse that policy for different scenarios by using different parameter values.

"parameters": {
"effectType": {
"type": "String",
"defaultValue": "Deny",
"allowedValues": [
"Audit",
"Deny",
"Disabled"
],
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policy"
}
}
},

note: the type value is case sensitive meaning that String needs to be written with a capital “S”.

Policy Rules

A policy rule consists of an if and then blocks.
The if block defines one or more conditions that specify when the policy is enforced. Logical operators can be applied to these conditions to precisely define the scenarios for a policy.

The then block defines the effect that happens when the if conditions are fulfilled.

For our Logic App we need to validate the field Microsoft.Web/serverFarms/sku.tier The sku.tier field is the field we identified in the properties at the Exploring properties section in this article. The value that we want to disallow is WorkflowStandard as this is the type used for the non-consumption based plan.

{
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Web/serverFarms"
},
{
"field": "Microsoft.Web/serverFarms/sku.tier",
"Equals": "WorkflowStandard"
}
]
}
}
}

The policy rule exists out of a couple of elements that I will explain a bit more to understand what we are doing.

Operators

There are 3 logic operators that can be used in an Azure Policy Definition
- allOf requires all conditions to be true
- anyOf requires one or more conditions to be true
- not Inverts the results of the condition

Conditions

A condition evaluates whether a field meets certain criteria.
To avoid doing a rewrite of the available documentation of Microsoft you can follow the link for more information here:
https://learn.microsoft.com/en-us/azure/governance/policy/concepts/definition-structure#conditions

Policy Effect

Now that we have our policy rule ready we can start to look at the policy effect. The policy effect describes what needs to happen if the policyRule returns as true

The following table gives shows a small summary of the different effects:
- Audit create a warning event in the activity log when not-compliant
- Deny Is used to prevent a resource request that doesn’t match the policy
- Disabled this disables the policy. useful for testing policies.

More in depth information about effects can be in the Microsoft documentation:
https://learn.microsoft.com/en-us/azure/governance/policy/concepts/effects#order-of-evaluation

For our policy effect we want to deny the deployment of the Logic App if the chosen App Service plan is not consumption based. In the code snippet below the parameter value is used to select the effect.

"then": {
"effect": "[parameters('effectType')]"
}

Putting it together

Now that we have gathered all bits and pieces we can start putting it all together and create the policy definition.

  1. We will start with the mode which we have set to Indexed to only target resources.
  2. The next step is to add the parameters (optional) where in this case we use it to select the effect.
  3. The last part in this example is the policyRule including the effect that contains the logic for our Policy Definition.
{
"mode": "Indexed",
"parameters": {
"effectType": {
"type": "String",
"defaultValue": "Deny",
"allowedValues": [
"Audit",
"Deny",
"Disabled"
],
"metadata": {
"displayName": "Effect",
"description": "Audit or Deny the deployment of the resource"
}
}
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Web/serverFarms"
},
{
"field": "Microsoft.Web/serverFarms/sku.tier",
"equals": "WorkflowStandard"
}
]
},
"then": {
"effect": "[parameters('effectType')]"
}
}
}

Testing the Policy

After creating and assigning the Logic App policy to our subscription we can start validating if it works as expected. You won’t see an error or restriction when configuring the Logic App in the wizard.

The validation of the policy is done at deployment time by the Azure Resource Manager.

Once the Create button is selected, The Azure resource manager will validate our deployment and eventually return an error message as shown below.

If you click on the deployment validation failed text in the notification center a very raw json error is shown with a little arrow at the left of the screen. When clicking on this arrow a more user friendly error is shown.

Wrap Up

Congratulations, you have mastered the skill to create an Azure Policy Definition.

What we have learned is that the most important thing of creating a policy is understand the resource and dependencies. In our example we created a policy to validate the deployment of a Logic App, but ended up with auditing the App Service Plan to reach our goal.

Second of all, try to use parameters where possible. This helps creating more flexible policy definitions and limits the amount of policies needed in the environment.

Happy coding!

--

--

Rogier Dijkman
Rogier Dijkman

Written by Rogier Dijkman

Microsoft Security MVP | Azure | GitHub | Cloud Security Architect | Marathoner | passionate about Microsoft Security

Responses (1)