Theory — Azure Policy
Theory — Azure Policy
This page covers the core concepts behind Azure Policy — how it works, what it does, and how it fits into Day 5's labs.
What Is Azure Policy?
Azure Policy lets you create rules that govern which resources can be created, how they should be configured, and what metadata they must have. It operates at two levels:
| Type | Description | Example |
|---|---|---|
| Built-in definitions | Pre-built policies maintained by Microsoft | "All VMs must use managed disks" |
| Custom definitions | Your own rules written in JSON | "All resources must have a cost-centre tag" |
Policy definitions are grouped into initiative definitions (also called policy sets), which bundle related policies together. An initiative definition is like a compliance profile — for example, the "CIS Azure Foundation Benchmark" initiative bundles multiple individual policies that implement the CIS Azure Foundation benchmark.
Policy Definition Structure
A custom policy definition looks like this:
{
"properties": {
"displayName": "Allowed Locations",
"policyType": "Custom",
"mode": "All",
"description": "Restrict the locations where resources can be created",
"metadata": {
"category": "Tags"
},
"parameters": {
"allowedLocations": {
"type": "Array",
"metadata": {
"displayName": "Allowed Locations",
"description": "List of Azure locations that resources can be deployed to"
}
}
},
"policyRule": {
"if": {
"field": "location",
"notIn": "[parameters('allowedLocations')]"
},
"then": {
"effect": "deny"
}
}
}
}The policyRule uses a conditional expression: if the condition is true, apply the effect. In this case, if the resource location is not in the allowed list, deny the creation.
Policy Effects
Azure Policy supports several effects that determine what happens when a policy rule evaluates to true:
| Effect | Description | When to Use |
|---|---|---|
| Deny | Blocks the resource from being created or modified | Enforcement — prevent non-compliant resources |
| Audit | Logs non-compliance but allows creation | Detection only — flag for review |
| DeployIfNotExists | Deploys a resource if it doesn't exist | Ensure required resources are present |
| Append | Adds fields to the resource before saving | Add metadata or configuration |
| Modify | Modifies resource properties during creation/update | Fix misconfigurations automatically |
| AuditIfNotExists | Audits if a related resource doesn't exist | Check for dependencies |
The Most Common Effects in Practice
Deny is the most familiar — it's what prevents someone from deploying an unencrypted storage account. But DeployIfNotExists and Modify are more interesting because they actually fix problems:
// DeployIfMissing example: ensure diagnostic settings exist
"then": {
"effect": "deployIfNotExists",
"details": {
"type": "Microsoft.Insights/diagnosticSettings",
"existenceCondition": {
"field": "name",
"equals": "default"
},
"deployment": {
"properties": {
"template": { ... }
}
}
}
}This policy checks if diagnostic settings exist on a resource. If they don't, it deploys them automatically — no human intervention required.
Policy Assignments: Scope and Exclusions
A policy definition is just a template. To make it effective, you assign it to a scope:
| Scope Level | Effect |
|---|---|
| Management Group | Applies across all subscriptions in the group — ideal for organisation-wide policies |
| Subscription | Applies to all resources in the subscription |
| Resource Group | Applies to resources within a specific group |
| Individual Resource | Applies only to that resource |
Exclusions
Not every resource should be covered by every policy. For example, you might want to deny public IP addresses everywhere except in a development subscription used for testing.
In the Azure portal:
- Navigate to Policy → Assignments
- Select the assignment you want to modify
- Click Exclusions and add the subscriptions or resource groups to exclude
Parameters
Policies can accept parameters, which makes them reusable. The "Allowed Locations" policy shown above accepts an allowedLocations parameter — different teams can assign it with different allowed regions without creating a new policy definition.
Azure Policy vs RBAC: Different Jobs
RBAC and Policy solve different problems — here's the distinction:
| Azure RBAC | Azure Policy | |
|---|---|---|
| Question | "Who can do what?" | "What should resources look like?" |
| Focus | Access control | Resource configuration |
| Example | "Alice can create VMs" | "All VMs must use managed disks" |
| When it runs | At the point of action (create/delete) | Continuously on all resources |
| Override | Owner can always override | Owner can always override, but audit trails remain |
RBAC controls who has permission to act. Policy controls what those actions can create. They work together:
Enforcing Best Practices and Minimum Requirements
Azure Policy isn't just about preventing violations — it's also about enforcing best practices and minimum requirements across every team, regardless of how they deploy. This is especially critical when teams are self-hosting their own infrastructure.
Self-Hosting Scenarios
For self-hosted infrastructure (on-premises VMs, hybrid setups), Azure Policy enforcement requires Azure Arc. Arc-enabled resources register as Azure resources and become subject to the same policy assignments. This covers Arc-enabled servers, Kubernetes clusters, and SQL instances — but only if they're connected to Azure.
Minimum Requirements Enforcement
Policies can enforce minimum requirements that every team must meet, regardless of their deployment method:
| Requirement | Policy Approach |
|---|---|
| Minimum TLS version for all endpoints | Deny resources with TLS < 1.2 |
| Minimum encryption standard for storage | Require customer-managed keys or Microsoft-managed encryption |
| Minimum tag coverage for cost allocation | Require tags (environment, owner, cost-centre) before creation |
| Monitoring baseline for all resources | DeployIfNotExists diagnostic settings on every resource type |
| Network isolation for production | Deny public IP assignment on production resources |
These aren't just "nice to have" — they're the floor, not the ceiling. Every team, whether they self-host or use cloud-native services, must meet these minimums.
Why Azure Policy Is Harder to Bypass Than CI/CD Checks
The CI/CD Bypass Problem
CI/CD pipelines and other pre-deployment checks are valuable, but they have a critical weakness: they can be bypassed.
Ways CI/CD checks can be bypassed:
| Method | How It Works |
|---|---|
| Direct portal deployment | Someone creates a resource directly in the Azure portal, bypassing the pipeline entirely |
| CLI/API calls | Someone uses az or Terraform outside of CI/CD to deploy resources |
| Local development | Developers test deployments locally without going through CI/CD |
| Emergency changes | "We need this now, we'll add the checks later" — urgency overrides process |
| Pipeline misconfiguration | The pipeline exists but isn't enforced as a gate (optional step instead of required) |
Why Azure Policy Is Different
Azure Policy is enforced at two points that are much harder to bypass:
1. At creation time (Deny effect)
When someone tries to create a non-compliant resource — whether via portal, CLI, API, or CI/CD — Azure Policy evaluates the request before the resource is created. This is a hard block, not a warning.
- Portal: "The operation failed because it does not meet policy requirements"
- CLI: "Resource deployment failed due to policy violation"
- Terraform: "Error: denied by policy"
This cannot be bypassed by changing your deployment method. No matter how you try to create the resource, Azure evaluates the policy before allowing the action.
2. After creation (Audit + Remediation)
Even if a resource is created compliantly but later drifts out of compliance — someone changes a setting in the portal, modifies a configuration file, or updates a pipeline — Azure Policy detects this through periodic evaluation and can auto-fix it via remediation tasks.
The combination of real-time enforcement at creation time and periodic drift detection after creation means that Azure Policy covers the full lifecycle — something CI/CD pipelines alone cannot do.
The Takeaway
CI/CD checks are valuable for catching issues early in the development process. But they are not a substitute for Azure Policy. Use both: CI/CD catches problems before deployment, Azure Policy catches them regardless of how deployment happens and continues enforcing after deployment.
Common Policy Scenarios
Scenario 1: Tagging Requirements
Every resource must have a cost-centre and environment tag. Without these tags, cost allocation becomes impossible.
Approach: Deploy a custom policy that denies resources without required tags — or use an Audit effect if you want visibility first.
Scenario 2: Encryption at Rest
All storage accounts must use customer-managed keys (CMK) for encryption.
Approach: Use the built-in "Storage accounts should use customer-managed keys" policy.
Scenario 3: Network Security
No public IP addresses in production subscriptions.
Approach: Custom policy that denies PublicIp address creation, with exclusions for dev/test environments.
Scenario 4: Monitoring Baseline
All resources must have diagnostic settings pointing to the central Log Analytics workspace.
Approach: DeployIfNotExists policy that creates diagnostic settings if they don't exist — this is the same pattern shown earlier in the Policy Effects section.
Policy Evaluation and Compliance State
When a policy is assigned, Azure evaluates all existing resources against it. This evaluation isn't instant — it can take several minutes or even hours for large subscriptions.
Compliance States
| State | Meaning |
|---|---|
| Compliant | Resource matches the policy definition |
| Non-compliant | Resource violates the policy definition |
| NotApplicable | Policy doesn't apply to this resource type or property |
| NotScanned | Evaluation hasn't run yet (usually transient) |
Drift Detection
When a resource is modified after initial evaluation, Azure detects the drift and updates the compliance state. However, this isn't real-time — there's a delay of several minutes to hours depending on subscription size and policy complexity.
To see compliance status:
- Navigate to Policy → Compliance in the Azure portal
- Select the policy or initiative definition
- View resources by compliance state
Remediation Tasks
For policies with DeployIfNotExists or Modify effects, you can create a remediation task that fixes non-compliant resources:
- Go to the policy assignment
- Click Remediation → Add remediation task
- Select non-compliant resources (or all of them)
- The task runs asynchronously and reports progress
Warning: Remediation tasks modify your infrastructure. Always review which resources will be affected before running a remediation task at scale.
Key Takeaways
- Azure Policy definitions are templates; assignments to scopes make them active.
- Different effects (Deny, Audit, DeployIfNotExists, Modify) serve different governance strategies.
- RBAC controls access; policy controls configuration. They work together but solve different problems.
- Compliance evaluation isn't instant — drift detection has a delay.
- Remediation tasks can automatically fix non-compliant resources, but use them carefully.
- Azure Policy enforces on any resource Azure can see, including self-hosted infrastructure via Azure Arc — not just cloud-native services.
- CI/CD checks can be bypassed; Azure Policy enforces at creation time and after creation, making it much harder to bypass.
Sources
- Azure Policy deny effect — Microsoft Learn