Creating a Custom Policy Initiative (Policy Set)
This guide explains how to create Azure Policy initiatives (also known as policy sets) in your Azure Landing Zone deployment. Initiatives allow you to group multiple policy definitions together for easier management and assignment.
ImportantYou will need to have a custom library if you want to add custom initiatives. Make sure you have completed that step before continuing.
An initiative (policy set) is a collection of policy definitions grouped together towards a specific goal. Benefits include:
- Simplified assignment: Assign multiple policies with a single assignment
- Easier compliance tracking: View compliance for related policies as a group
- Parameter management: Define parameters once and pass them to multiple policies
- Versioning: Update multiple policies by updating the initiative
Initiatives in the ALZ library follow a specific naming convention and structure:
- Filename:
<InitiativeName>.alz_policy_set_definition.json(or.yaml) - Location:
lib/policy_set_definitions/folder in your custom library
{
"name": "My-Initiative-Name",
"type": "Microsoft.Authorization/policySetDefinitions",
"properties": {
"displayName": "My Initiative Display Name",
"description": "Description of what this initiative does.",
"policyType": "Custom",
"metadata": {
"version": "1.0.0",
"category": "General",
"source": "https://github.com/Azure/Enterprise-Scale/",
"alzCloudEnvironments": [
"AzureCloud",
"AzureChinaCloud",
"AzureUSGovernment"
]
},
"parameters": {
// Initiative-level parameters defined here
},
"policyDefinitions": [
// Policy definitions included in the initiative
]
}
}
The policyDefinitions array contains references to the policies included in your initiative. Each policy reference requires:
| Field | Description |
|---|---|
policyDefinitionId | Resource ID of the policy definition |
policyDefinitionReferenceId | Unique identifier for this reference within the initiative |
parameters | Parameter values to pass to the policy definition |
groupNames | (Optional) Array of group names for organizing policies |
For built-in policies, use the full Azure resource ID:
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-Tag-On-Resources",
"parameters": {
"tagName": {
"value": "Environment"
}
}
}
TipFind built-in policy definition IDs using AzAdvertizer or the Azure Portal.
For custom policies, use a placeholder management group path. The ALZ provider automatically resolves the correct resource ID:
{
"policyDefinitionId": "/providers/Microsoft.Management/managementGroups/placeholder/providers/Microsoft.Authorization/policyDefinitions/Deny-Resource-Types",
"policyDefinitionReferenceId": "Deny-Unapproved-Resource-Types",
"parameters": {
"effect": {
"value": "[parameters('effect')]"
}
}
}
NoteThe policy definition name (e.g.,Deny-Resource-Types) must match thenamefield in your custom policy definition file.
You can include the same policy definition multiple times with different parameters. Each reference must have a unique policyDefinitionReferenceId:
"policyDefinitions": [
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-Environment-Tag",
"parameters": {
"tagName": { "value": "Environment" }
}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-Owner-Tag",
"parameters": {
"tagName": { "value": "Owner" }
}
}
]
Initiative parameters allow you to configure policies at assignment time rather than hardcoding values.
Define parameters at the initiative level in the parameters object:
"parameters": {
"effect": {
"type": "String",
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policies"
},
"allowedValues": ["Audit", "Deny", "Disabled"],
"defaultValue": "Audit"
},
"tagName": {
"type": "String",
"metadata": {
"displayName": "Tag Name",
"description": "The name of the required tag"
},
"defaultValue": "Environment"
}
}
Use the [parameters('parameterName')] syntax to pass initiative parameters to policy definitions:
{
"policyDefinitionId": "/providers/Microsoft.Management/managementGroups/placeholder/providers/Microsoft.Authorization/policyDefinitions/Deny-Resource-Types",
"policyDefinitionReferenceId": "Deny-Unapproved-Resource-Types",
"parameters": {
"effect": {
"value": "[parameters('effect')]"
},
"deniedResourceTypes": {
"value": "[parameters('deniedResourceTypes')]"
}
}
}
When multiple policies in your initiative share the same parameter (like effect), you have two approaches:
Define one parameter that applies to all policies:
"parameters": {
"effect": {
"type": "String",
"defaultValue": "Audit",
"allowedValues": ["Audit", "Deny", "Disabled"]
}
},
"policyDefinitions": [
{
"policyDefinitionReferenceId": "Policy-A",
"parameters": { "effect": { "value": "[parameters('effect')]" } }
},
{
"policyDefinitionReferenceId": "Policy-B",
"parameters": { "effect": { "value": "[parameters('effect')]" } }
}
]
When you need granular control, define separate parameters for each policy:
"parameters": {
"policyAEffect": {
"type": "String",
"defaultValue": "Audit",
"allowedValues": ["Audit", "Deny", "Disabled"]
},
"policyBEffect": {
"type": "String",
"defaultValue": "Deny",
"allowedValues": ["Audit", "Deny", "Disabled"]
}
},
"policyDefinitions": [
{
"policyDefinitionReferenceId": "Policy-A",
"parameters": { "effect": { "value": "[parameters('policyAEffect')]" } }
},
{
"policyDefinitionReferenceId": "Policy-B",
"parameters": { "effect": { "value": "[parameters('policyBEffect')]" } }
}
]
TipUse a single shared parameter when policies should be enabled/disabled together. Use individual parameters when you need to control policies independently.
This example creates an initiative using only built-in policies to enforce mandatory tags.
NoteThe built-in policy871b6d14-10aa-478d-b590-94f262ecfa99(“Require a tag on resources”) has a fixed Deny effect and only accepts thetagNameparameter. Always check policy parameters on AzAdvertizer before including them in initiatives.
Create Enforce-Mandatory-Tags.alz_policy_set_definition.json:
{
"name": "Enforce-Mandatory-Tags",
"type": "Microsoft.Authorization/policySetDefinitions",
"properties": {
"displayName": "Enforce mandatory tags on resources",
"description": "This initiative enforces mandatory tags on all resources to ensure proper governance and cost management.",
"policyType": "Custom",
"metadata": {
"version": "1.0.0",
"category": "Tags",
"source": "https://github.com/Azure/Enterprise-Scale/",
"alzCloudEnvironments": [
"AzureCloud",
"AzureChinaCloud",
"AzureUSGovernment"
]
},
"parameters": {
"environmentTagName": {
"type": "String",
"metadata": {
"displayName": "Environment Tag Name",
"description": "Name of the environment tag"
},
"defaultValue": "Environment"
},
"ownerTagName": {
"type": "String",
"metadata": {
"displayName": "Owner Tag Name",
"description": "Name of the owner tag"
},
"defaultValue": "Owner"
},
"costCenterTagName": {
"type": "String",
"metadata": {
"displayName": "Cost Center Tag Name",
"description": "Name of the cost center tag"
},
"defaultValue": "CostCenter"
}
},
"policyDefinitions": [
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-Environment-Tag",
"parameters": {
"tagName": {
"value": "[parameters('environmentTagName')]"
}
}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-Owner-Tag",
"parameters": {
"tagName": {
"value": "[parameters('ownerTagName')]"
}
}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-CostCenter-Tag",
"parameters": {
"tagName": {
"value": "[parameters('costCenterTagName')]"
}
}
}
]
}
}
This example creates an initiative using the custom Deny-Resource-Types policy from Creating a Custom Azure Policy Definition.
First, ensure you have created Deny-Resource-Types.alz_policy_definition.json in your lib/policy_definitions/ folder.
Create Enforce-Resource-Restrictions.alz_policy_set_definition.json:
{
"name": "Enforce-Resource-Restrictions",
"type": "Microsoft.Authorization/policySetDefinitions",
"properties": {
"displayName": "Enforce resource restrictions",
"description": "This initiative enforces resource restrictions by denying unapproved resource types.",
"policyType": "Custom",
"metadata": {
"version": "1.0.0",
"category": "Governance",
"source": "https://github.com/Azure/Enterprise-Scale/",
"alzCloudEnvironments": [
"AzureCloud",
"AzureChinaCloud",
"AzureUSGovernment"
]
},
"parameters": {
"effect": {
"type": "String",
"metadata": {
"displayName": "Effect",
"description": "Enable or disable the execution of the policies"
},
"allowedValues": ["Audit", "Deny", "Disabled"],
"defaultValue": "Deny"
},
"deniedResourceTypes": {
"type": "Array",
"metadata": {
"displayName": "Denied Resource Types",
"description": "The list of resource types that are not allowed to be deployed",
"strongType": "resourceTypes"
},
"defaultValue": [
"Microsoft.ClassicCompute/virtualMachines",
"Microsoft.ClassicStorage/storageAccounts",
"Microsoft.ClassicNetwork/virtualNetworks"
]
}
},
"policyDefinitions": [
{
"policyDefinitionId": "/providers/Microsoft.Management/managementGroups/placeholder/providers/Microsoft.Authorization/policyDefinitions/Deny-Resource-Types",
"policyDefinitionReferenceId": "Deny-Unapproved-Resource-Types",
"parameters": {
"effect": {
"value": "[parameters('effect')]"
},
"deniedResourceTypes": {
"value": "[parameters('deniedResourceTypes')]"
}
}
}
]
}
}
ImportantWhen your initiative includes custom policies, you must add both the policy definition and the initiative to your archetype. The policy definition should be added viapolicy_definitions_to_addand the initiative viapolicy_set_definitions_to_add.
This example demonstrates how to combine the tagging policies and resource restriction policies into a single comprehensive governance initiative. This approach promotes modularity and reusability by consolidating related governance controls.
Create Enforce-Governance-Standards.alz_policy_set_definition.json:
{
"name": "Enforce-Governance-Standards",
"type": "Microsoft.Authorization/policySetDefinitions",
"properties": {
"displayName": "Enforce governance standards",
"description": "This initiative combines tagging requirements and resource restrictions into a comprehensive governance policy set.",
"policyType": "Custom",
"metadata": {
"version": "1.0.0",
"category": "Governance",
"source": "https://github.com/Azure/Enterprise-Scale/",
"alzCloudEnvironments": [
"AzureCloud",
"AzureChinaCloud",
"AzureUSGovernment"
]
},
"parameters": {
"resourceRestrictionEffect": {
"type": "String",
"metadata": {
"displayName": "Resource Restriction Effect",
"description": "Effect for resource restriction policies"
},
"allowedValues": ["Audit", "Deny", "Disabled"],
"defaultValue": "Deny"
},
"environmentTagName": {
"type": "String",
"metadata": {
"displayName": "Environment Tag Name",
"description": "Name of the environment tag"
},
"defaultValue": "Environment"
},
"ownerTagName": {
"type": "String",
"metadata": {
"displayName": "Owner Tag Name",
"description": "Name of the owner tag"
},
"defaultValue": "Owner"
},
"costCenterTagName": {
"type": "String",
"metadata": {
"displayName": "Cost Center Tag Name",
"description": "Name of the cost center tag"
},
"defaultValue": "CostCenter"
},
"deniedResourceTypes": {
"type": "Array",
"metadata": {
"displayName": "Denied Resource Types",
"description": "The list of resource types that are not allowed to be deployed",
"strongType": "resourceTypes"
},
"defaultValue": [
"Microsoft.ClassicCompute/virtualMachines",
"Microsoft.ClassicStorage/storageAccounts",
"Microsoft.ClassicNetwork/virtualNetworks"
]
},
"allowedLocations": {
"type": "Array",
"metadata": {
"displayName": "Allowed Locations",
"description": "The list of locations that resources can be deployed to"
},
"defaultValue": [
"eastus",
"eastus2",
"westus2",
"westeurope",
"northeurope"
]
}
},
"policyDefinitionGroups": [
{
"name": "Tagging",
"displayName": "Tagging Policies",
"description": "Policies related to resource tagging requirements"
},
{
"name": "ResourceRestrictions",
"displayName": "Resource Restriction Policies",
"description": "Policies that restrict resource deployments"
}
],
"policyDefinitions": [
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-Environment-Tag",
"groupNames": ["Tagging"],
"parameters": {
"tagName": {
"value": "[parameters('environmentTagName')]"
}
}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-Owner-Tag",
"groupNames": ["Tagging"],
"parameters": {
"tagName": {
"value": "[parameters('ownerTagName')]"
}
}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/871b6d14-10aa-478d-b590-94f262ecfa99",
"policyDefinitionReferenceId": "Require-CostCenter-Tag",
"groupNames": ["Tagging"],
"parameters": {
"tagName": {
"value": "[parameters('costCenterTagName')]"
}
}
},
{
"policyDefinitionId": "/providers/Microsoft.Management/managementGroups/placeholder/providers/Microsoft.Authorization/policyDefinitions/Deny-Resource-Types",
"policyDefinitionReferenceId": "Deny-Unapproved-Resource-Types",
"groupNames": ["ResourceRestrictions"],
"parameters": {
"effect": {
"value": "[parameters('resourceRestrictionEffect')]"
},
"deniedResourceTypes": {
"value": "[parameters('deniedResourceTypes')]"
}
}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/e56962a6-4747-49cd-b67b-bf8b01975c4c",
"policyDefinitionReferenceId": "Allowed-Locations",
"groupNames": ["ResourceRestrictions"],
"parameters": {
"listOfAllowedLocations": {
"value": "[parameters('allowedLocations')]"
}
}
}
]
}
}
Policy Groups: The
policyDefinitionGroupsfield organizes policies into logical categories (Tagging, ResourceRestrictions). This improves compliance reporting and makes the initiative easier to understand.Effect Parameters: The
resourceRestrictionEffectparameter allows you to control the enforcement level for resource restriction policies. Note that the tagging policy871b6d14-10aa-478d-b590-94f262ecfa99has a fixed Deny effect and does not accept an effect parameter.Mixed Policy Sources: The initiative includes:
- Built-in policy
871b6d14-10aa-478d-b590-94f262ecfa99(Require a tag on resources) - fixed Deny effect - Built-in policy
e56962a6-4747-49cd-b67b-bf8b01975c4c(Allowed locations) - Custom policy
Deny-Resource-Types
- Built-in policy
When building initiatives, consider these patterns for reusability:
Create focused initiatives for specific compliance areas:
lib/policy_set_definitions/
├── Enforce-Tagging-Standards.alz_policy_set_definition.json
├── Enforce-Network-Security.alz_policy_set_definition.json
├── Enforce-Data-Protection.alz_policy_set_definition.json
└── Enforce-Identity-Security.alz_policy_set_definition.json
Benefits:
- Assign only what’s needed to each management group
- Easier to troubleshoot compliance issues
- Independent versioning and updates
Create a base initiative and extend it for specific scenarios:
lib/policy_set_definitions/
├── Governance-Baseline.alz_policy_set_definition.json # Core policies for all workloads
├── Governance-Production.alz_policy_set_definition.json # Baseline + stricter controls
└── Governance-Development.alz_policy_set_definition.json # Baseline + relaxed controls
TipWhen deciding between separate initiatives or a combined initiative:
- Separate initiatives: Better when different management groups need different policy combinations
- Combined initiatives: Better when policies should always be deployed together and you want simplified assignment management
After creating the initiative file, add it to an archetype using an override file.
Create or update root_override.alz_archetype_override.yaml in your lib/archetype_definitions/ folder:
name: "root_override"
base_archetype: "root"
policy_assignments_to_add: []
policy_assignments_to_remove: []
policy_definitions_to_add:
- "Deny-Resource-Types"
policy_definitions_to_remove: []
policy_set_definitions_to_add:
- "Enforce-Governance-Standards"
policy_set_definitions_to_remove: []
role_definitions_to_add: []
role_definitions_to_remove: []
NoteAdding an initiative to an archetype only deploys the initiative definition. To enforce it, you must also create and assign a policy assignment. See Creating an Azure Policy Assignment for details on assigning initiatives.
After creating initiatives, your library structure should look like this:
lib/
├── alz_library_metadata.json
├── architecture_definitions/
│ └── alz.alz_architecture_definition.yaml
├── archetype_definitions/
│ └── root_override.alz_archetype_override.yaml
├── policy_assignments/
│ └── Enforce-Governance-Standards.alz_policy_assignment.json
├── policy_definitions/
│ └── Deny-Resource-Types.alz_policy_definition.json
└── policy_set_definitions/
└── Enforce-Governance-Standards.alz_policy_set_definition.json
