Implement Azure Policy

Learn how to implement Azure Policy for governance in this hands-on Azure tutorial. Step-by-step guidance, troubleshooting, and what to learn next.

Focus: implement azure policy for governance

Sponsored

You've built and deployed Azure resources, but how do you prevent your team from accidentally (or intentionally) creating a VM that's exposed to the internet or a storage account with public access? Without guardrails, your cloud environment can spiral into a compliance nightmare, with security risks and ballooning costs. Azure Policy is the governance mechanism that enforces your standards — think of it as a set of rules that Azure automatically checks every time a resource is created. In this lesson, you'll learn how to implement Azure Policy for governance and start enforcing compliance across your subscriptions.

The problem this lesson solves

In a busy Azure environment, things go wrong: a developer spins up a VM with a public IP, a storage account gets created with anonymous blob access, or a resource gets deployed in the wrong region. Individually, these are minor mistakes; collectively, they become security holes and budget overruns. Azure Policy solves this by acting as a gate between the intent (an ARM template, CLI command, or portal action) and the actual resource creation. Once a policy is assigned, Azure evaluates every 'create' or 'update' request against your rules. If the request violates the policy, it's either denied outright or flagged as non-compliant — depending on the effect you chose. Azure Policy turns your governance principles from a set of best-practice documents into enforced, automated controls.

Core concept / mental model

Think of Azure Policy like the building code for your Azure house. When a contractor wants to add a room, the city checks their plans against the code before granting a permit. If the room violates the code, the permit is denied. Similarly, Azure Policy is a code of rules for your cloud resources. You define these rules as policy definitions, group them into initiatives, and then assign them to a scope (management group, subscription, or resource group). When someone tries to create or update a resource, Azure evaluates the request against the assigned policies. If a policy's condition isn't met, the defined effect (like Deny) kicks in — the resource is blocked, and the user gets a clear error message.

Here are the key terms:

  • Policy definition: A single rule, written in JSON, that describes a condition (e.g., "the resource's region must be eastus") and an effect (Deny, Audit, Append, etc.).
  • Initiative: A collection of policy definitions, often called a policy set. You can assign an initiative to apply multiple rules at once.
  • Assignment: The act of applying a policy or initiative to a particular scope. You can also use exemptions to exclude specific resources from a policy.

Here's a simplified diagram of the flow:

User request (create/update resource)
        ↓
Azure Resource Manager
        ↓
Policy evaluation (assigned policies)
        ↓
If condition matches → apply effect (Deny / Audit / etc.)
        ↓
Resource created (or denied) & compliance recorded

How it works step by step

Let's break down the lifecycle of an Azure Policy rule:

  1. Define the policy: You write a JSON document that specifies a policyRule. In that rule, you define the if condition (e.g., "if the resource is a VM and its size is not in the approved list") and the then effect (e.g., Deny).
  2. Group into an initiative (optional): Instead of assigning many individual rules, you can combine related definitions into an initiative. This helps with organization and reporting.
  3. Assign the policy/initiative: You select a scope (say, your subscription) and assign the policy. During assignment, you can set parameters (e.g., the list of allowed VM sizes) and choose an effect.
  4. Evaluate existing resources: After assignment, Azure starts a scan of existing resources. Any resource that doesn't meet the policy condition is marked non-compliant.
  5. Enforce on new resources: For new requests, Azure evaluates the policy before the resource is created. If the effect is Deny, the request is blocked; if it's Audit, the resource is created but flagged as non-compliant.
  6. Remediate: For non-compliant resources, you can manually fix them, or set up remediation tasks for policies that support deployIfNotExists or modify effects.

Defining a policy in JSON

Here's a basic example of a policy that denies any resource that isn't in the eastus region:

{
  "properties": {
    "displayName": "Ensure resources are in eastus",
    "description": "Denies resource creation outside the eastus region.",
    "policyRule": {
      "then": {
        "effect": "Deny"
      },
      "if": {
        "field": "location",
        "notIn": "[parameters('allowedLocations')]"
      }
    },
    "parameters": {
      "allowedLocations": {
        "type": "Array",
        "metadata": {
          "displayName": "Allowed locations",
          "description": "The list of allowed locations for resources."
        }
      }
    }
  }
}

Assigning a policy via CLI

Once you have your definition (or use a built-in one), you assign it with the Azure CLI:

# Create a policy definition from a JSON file (optional)
az policy definition create --name "allow-eastus-only" \
  --rules allallowed-locations.json \
  --mode Indexed

# Assign the policy to your subscription
az policy assignment create \
  --name "enforce-eastus" \
  --policy "allow-eastus-only" \
  --scope "/subscriptions/<subscription-id>" \
  --params '{"allowedLocations":["eastus"]}'  # if your definition has parameters

Viewing compliance

After assignment and a few minutes, you can see the compliance state:

az policy state summarize --policy-assignment-name "enforce-eastus"

This will give you a summary of how many resources are compliant vs non-compliant.

Hands-on walkthrough

Let's implement a real-world governance policy: deny creation of storage accounts with public blob access. Follow along — you'll need the Azure CLI and a subscription.

Step 1: Create a policy definition

Save the following JSON as deny-public-blob.json:

{
  "properties": {
    "displayName": "Deny public blob access for storage accounts",
    "description": "Prevents storage accounts from allowing anonymous blob access.",
    "policyRule": {
      "if": {
        "field": "type",
        "equals": "Microsoft.Storage/storageAccounts"
      },
      "then": {
        "effect": "Deny"
      },
      "if": {
        "allOf": [
          { "field": "type", "equals": "Microsoft.Storage/storageAccounts" },
          { "field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess", "equals": true }
        ]
      }
    }
  }
}

Note: The above JSON is simplified; in practice you'd need a proper if structure. For a complete example, see the Azure Policy samples.

Step 2: Create the definition

az policy definition create \
  --name "deny-public-blob" \
  --rules deny-public-blob.json \
  --mode Indexed

Step 3: Assign to your subscription

az policy assignment create \
  --name "enforce-no-public-blob" \
  --policy "deny-public-blob" \
  --scope "/subscriptions/<your-subscription-id>"

Step 4: Test it

Try to create a storage account with public blob access enabled:

az storage account create \
  --name mystorageaccount123 \
  --resource-group my-rg \
  --location eastus \
  --allow-blob-public-access true

Expected output (in red):

Deployment failed because the resource was denied by policy.
// etc.

If you try with --allow-blob-public-access false (or omit the flag, since the default is false), the creation succeeds.

Step 5: Check compliance

Wait a few minutes, then run:

az policy state list --policy-assignment-name "enforce-no-public-blob"

You'll see a list of existing storage accounts and their compliance state (compliant or non-compliant).

Pro tip: Use the --skip and --top parameters to paginate through many resources.

Compare options / when to choose what

Azure Policy uses effects to define what happens when a resource doesn't conform. Here's a table of the most common effects and when to use them:

Effect Behavior When to use
Audit Creates a warning in activity log, but doesn't block creation. When you want to detect issues without disrupting existing workflows. Great for a first rollout.
Deny Blocks the resource creation/update entirely. For strict, must-happen rules (e.g., no public IP, only approved VM sizes).
Append Adds fields to the resource (e.g., a tag) automatically, doesn't block. For required tags or to enforce a default value, ensuring consistency.
DeployIfNotExists Deploys a resource (e.g., a diagnostic setting) if it doesn't exist, then audits. For governance that requires a companion resource, like enabling logging on a storage account.
Modify Updates a property of existing resources (similar to Append but for existing resources). For auto-remediation of misconfigured settings, like setting a security option.

Choosing the right effect is a key design decision. Start with Audit to understand your environment's current compliance. Once you're confident, switch to Deny for strict enforcement.

Built-in vs custom definitions

Azure offers hundreds of built-in policies for common scenarios (like allowed locations, VM size restrictions, etc.). Use them when they fit, and create custom definitions only when you have unique requirements. This saves time and reduces maintenance.

Troubleshooting & edge cases

Here are common issues you may encounter when implementing Azure Policy:

  • Policy doesn't seem to take effect: Check that the assignment's scope covers the resource group you're testing. Also, policy evaluation can take up to 30 minutes for existing resources, but new ones are evaluated immediately.
  • Denied resource, but I need to create it anyway: Use exemptions to temporarily exclude a specific resource. Be careful — this weakens your governance, so it should be a deliberate, time-bound decision.
  • field not found error in policy definition: You might be referencing a property that doesn't exist. Ensure the field name matches the resource type's schema. Use az provider show --namespace Microsoft.Storage to inspect aliases.
  • Policy assignment fails due to invalid parameters: Double-check your parameter names and types. If your definition expects allowedLocations as an array, you must pass an array in --params.
  • Compliance shows non-compliant resources after a Deny policy: Existing resources aren't deleted by Deny. You must manually fix them or use Modify/DeployIfNotExists for auto-remediation.
  • CLI error: 'Invalid policy rule format': Validate your JSON using az policy definition create with a well-formed rule. Use online JSON validators to catch syntax errors.

What you learned & what's next

You've now mastered the core of implementing Azure Policy for governance. You can define a policy, assign it, test its enforcement, and interpret compliance results. You understand the difference between Audit, Deny, and other effects, and you know how to troubleshoot common issues. This hands-on skill is essential for any DevOps engineer aiming to keep Azure environments secure and aligned with company standards.

In the next lesson, you'll explore Azure Blueprints (or perhaps Management Groups if that's the next step in your track) to organize your subscriptions into a hierarchy and apply policies via blueprints. This will let you operationalize these governance controls across your entire organization.

Key takeaway: Azure Policy is your guardrail for Azure — always think of governance as code, and implement policies early to prevent bad practices from spreading.

Practice recap

Create a new policy definition that denies public network access for storage accounts (or another common rule like requiring a resource tag). Assign it to a test resource group and write a script that creates a non-compliant resource to confirm it's blocked. Then extend this by adding an Audit effect and observing the compliance report for an existing resource.

Common mistakes

  • Assigning a policy with the Deny effect before auditing your existing resources — you'll block legitimate operations once the policy goes live.
  • Forgetting to scope assignments to the correct management group or subscription — the policy silently doesn't affect resources outside that scope.
  • Using Audit instead of Deny for truly mandatory rules, giving a false sense of security because new non-compliant resources are still created.
  • Writing custom policy definitions with incorrect field names or missing aliases, causing runtime evaluation errors with cryptic messages.
  • Not allowing time for compliance evaluation — new resources are immediate, but existing resources may take up to 30 minutes to show updated compliance status.

Variations

  1. Use built-in policies instead of custom definitions — many common scenarios are already covered, saving you time and maintenance.
  2. Create initiatives (policy sets) to bundle multiple rules into one logical unit for easier assignment and reporting.
  3. Use Azure Policy as Code with IaC tools like Bicep or Terraform to version control your policy definitions and assignments just like any other app code.

Real-world use cases

  • Enforce that all resources have required owner and cost-center tags to simplify cost management and accountability.
  • Deny creation of VMs outside approved SKU sizes to control cloud spend and ensure consistent performance.
  • Automatically append a diagnostic setting to every new storage account to ensure logs ship to a central analytics workspace.

Key takeaways

  • Azure Policy acts as a gatekeeper, intercepting resource creation and updates to enforce your governance rules.
  • The effect you choose defines the outcome: Audit for visibility, Deny for blocking, Append for adding fields, and Modify for auto-remediation.
  • Policy definitions are written in JSON and can be grouped into initiatives for easier management.
  • Assignments are scoped to either management groups, subscriptions, or resource groups, giving you granular control.
  • Compliance evaluation happens immediately for new resources, while existing ones may take up to 30 minutes to reflect in reports.
  • Always start with Audit to gauge your environment before enforcing Deny to avoid breaking existing workflows.

Sponsored

Sponsored

Discussion

Questions, corrections, and tips help everyone reading this page.

0 comments

Add a comment

Shown publicly with your comment.

Be constructive · max 4,000 characters

No comments yet — start the thread.

Related tutorials, quizzes, and articles for this topic.