Assign policy definitions from Azure landing zones Terraform module
A little while back I spent an hour or so writing an Azure policy, only to discover that the Azure landing zones Terraform module already has a policy definition that does exactly what I wanted to accomplish, but no assignments linked to it. It took me another hour of confusion and frustration to figure out how to actually assign the policy, as there is a step I completely overlooked. So here is a quick summary of how to use these policies so you can save yourself the trouble.
Set the library_path
First off we need to make sure the [library_path
}(https://github.com/Azure/terraform-azurerm-caf-enterprise-scale/wiki/%5BUser-Guide%5D-Module-Variables) variable is set to make sure the Azure landing zones-module can find our customizations. This is a requirement for adding custom landing zone archetypes, policy definitions, policy assignments etc.
First, create a new folder named lib
in the root of your Terraform project. Then, in the main.tf
-file, add the following line to your module block:
1module "azure_landing_zones_architecture" {
2 source = "Azure/caf-enterprise_scale/azurerm"
3 version = "3.1.2"
4 library_path = "${path.root}/lib"
5}
Create the policy assignment
Under the lib
-folder, create a new folder named policy_assignments
and add a new file named policy_assignment_<name of policy>.tf
. The Azure landing zones-module will pick up all files prefixed with policy_assignment_
, but the rest of the name is only important for your own mental health. Name if something useful and related to what the policy does.
In the example below I use the Deny vNet peering to non-approved vNets-policy to only allow spoke networks to peer with the hub network. You can see a complete list of policy definitions in GitHub
1{
2 "name": "Restrict_VNet_Peering",
3 "type": "Microsoft.Authorization/policyAssignments",
4 "apiVersion": "2019-09-01",
5 "properties": {
6 "description": "This policy denies the creation of vNet Peerings to non-approved vNets under the assigned scope",
7 "displayName": "Deny vNet peering to non-approved vNets",
8 "notScopes": [],
9 "parameters": {
10 } ,
11 "policyDefinitionId": "${root_scope_resource_id}/providers/Microsoft.Authorization/policyDefinitions/Deny-VNET-Peering-To-Non-Approved-VNETs",
12 "scope": "${current_scope_resource_id}",
13 "enforcementMode": null,
14 "nonComplianceMessages": [
15 {
16 "message": "VNet peering to non-approved vNets is not allowed"
17 }
18 ]
19 },
20 "location": "${default_location}",
21 "identity": {
22 "type": "SystemAssigned"
23 }
24}
The important lines to change from my example are highlighted in the code block:
# | Parameter | Description |
---|---|---|
2 | name | The name used to reference the policy assignment from an archetype definition or archetype extension. Note that the max length is 24 characters |
6 | description | A short description of what the policy does |
7 | displayName | More human friendly name to make things easier to find the the Azure portal |
11 | policyDefinitionId | The resource ID of the policy definition. A good practice is to keep most definitions in the root scope. Keep ${root_scope_resource_id}/providers/Microsoft.Authorization/policyDefinitions/ and append the string with the name of the policy definition you want to use |
16 | message | The message that will be displayed in the Azure portal if the policy is violated |
Assign the policy
Finally, we need to actually assign the policy to a management group. We have two different options to achieve this:
- We can extend a built in archetype definition (e.g. “corp” or “online”)
- We can create a custom landing zone archetype
In the example below I use the second option and create my own custom landing zone. You can follow the link to get a complete example on how this is created. The only difference from that example is the policy assignment in lib/archetype_definition_customer_online.json
:
1{
2 "customer_online": {
3 "policy_assignments": ["Restrict_VNet_Peering"],
4 "policy_definitions": [],
5 "policy_set_definitions": [],
6 "role_definitions": [],
7 "archetype_config": {
8 "parameters": {
9 },
10 "access_control": {}
11 }
12 }
13 }
We also have two different options for setting the policy parameters (i.e. the list of allowed virtual networks):
Create the list directly in our archetype definition/extension file:
1"parameters": {
2 "allowedVnets": {
3 "value": [
4 "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/rg-hub/providers/Microsoft.Network/virtualNetworks/vnet-hub"
5 ]
6 }
7}
Pass in the parameters in the terraform module:
1archetype_config = {
2 archetype_id = "customer_online"
3 parameters = {
4 Restrict_VNet_Peering = {
5 allowedVnets = [
6 data.azurerm_virtual_network.hub.id
7 ]
8 }
9 }
10}
I prefer the second method, as it gives me a nice and clean method of dynamically getting the resource ID by using Data Source: azurerm_virtual_network