azure | AZ-104 | | 2 views

Creating and Assigning Custom Azure Policies

  • azure-policy
  • management-and-governance

Understanding what a policy definition and assignment are conceptually is one thing; writing and deploying one is another. This walks through a small, realistic example — denying (or auditing) storage accounts that don't enforce HTTPS — first through the portal, then through PowerShell and Cloud Shell.

Writing the policy definition

A custom policy definition is a JSON document with a display name, a description, and a policyRule — the condition to check and the effect to apply. This one denies (or audits, depending on a parameter) storage accounts that don't have supportsHttpsTrafficOnly set to true:

{
  "properties": {
    "displayName": "Deny unencrypted storage accounts",
    "description": "This policy denies the creation of storage accounts without HTTPS traffic enabled.",
    "mode": "Indexed",
    "policyRule": {
      "if": {
        "allOf": [
          {
            "field": "type",
            "equals": "Microsoft.Storage/storageAccounts"
          },
          {
            "field": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
            "notEquals": true
          }
        ]
      },
      "then": {
        "effect": "[parameters('effectType')]"
      }
    },
    "parameters": {
      "effectType": {
        "type": "String",
        "metadata": {
          "displayName": "Effect Type",
          "description": "Determine whether to deny or audit non-compliant resources."
        },
        "defaultValue": "Deny"
      }
    }
  }
}

The policyRule checks two things together (allOf): that the resource type is Microsoft.Storage/storageAccounts, and that supportsHttpsTrafficOnly isn't true. If both hold, the effect defined by the effectType parameter applies — defaulting to Deny, but switchable to Audit at assignment time without editing the definition itself. Parameterising the effect like this is what makes a single definition usable in both "just tell me" (audit) and "actually stop it" (deny) modes.

Assigning the policy through the portal

  1. Create the policy definition. In the Azure Policy service, go to Definitions and create a new definition from the JSON above.
  2. Assign the policy. Assign the new definition to a resource group — a production resource group, say, where you actually want HTTPS enforced.
  3. Configure the parameters. During assignment, set effectType to Deny to block non-compliant storage accounts outright, or Audit to log them without blocking anything.

Common issues

  • Policy not applying. Check that the assignment is scoped to the resources you're actually testing against, and that the assignment's parameters are set the way you expect.
  • Incorrect property name. supportsHttpsTrafficOnly needs to match the storage account's actual ARM property name exactly — a typo here means the condition silently never matches.
  • JSON syntax errors. Run the definition through a JSON validator before creating it; malformed JSON fails at creation, not evaluation.
  • Propagation delay. Policy assignments aren't instantaneous — allow time for the assignment to propagate and evaluate against existing and new resources, and refresh the compliance view rather than assuming a stale result is final.
  • Scope issues. If the policy needs to apply across multiple subscriptions, the assignment needs to be scoped to a management group that contains all of them — a resource-group-level assignment can't reach across subscription boundaries.

Assigning a policy with PowerShell and Cloud Shell

The same assignment can be done from Azure Cloud Shell (a browser-based shell with PowerShell and the Azure CLI preinstalled, no local setup needed) using the Az PowerShell module's policy cmdlets.

Create a resource group for the lab:

New-AzResourceGroup -Name Staging_VMs -Location CentralUS

Store the resource group object for reuse in later commands:

$rg = Get-AzResourceGroup -Name Staging_VMs -Location CentralUS

Retrieve an existing policy definition by its ID (found in the Azure portal's policy definitions section):

$definition = Get-AzPolicyDefinition -Id "/subscriptions/{subscriptionId}/providers/Microsoft.Authorization/policyDefinitions/{policyDefinitionId}"

Assign the retrieved definition to the resource group:

New-AzPolicyAssignment -Scope $rg.ResourceId -PolicyDefinition $definition -Name RGLocationMatch -DisplayName "ResourceGroupMatches resources location"

-Scope takes the resource group's ResourceId, -PolicyDefinition takes the definition object retrieved above, and -Name/-DisplayName give the assignment a human-readable identity.

Common issues

  • Cloud Shell storage account creation failure. Cloud Shell prompts to create a storage account on first use; if creation fails, try a different, more unique name.
  • Policy definition ID not found. Double-check the ID copied from the portal — a missing character or a mismatched subscription is the usual cause.
  • Insufficient permissions. Creating resource groups and assigning policies needs Owner or User Access Administrator (or an equivalent custom role) on the target scope.
  • Variable not recognised. Usually means an earlier command in the sequence didn't actually run or assign the variable — check the preceding step rather than the one that errored.
  • Azure's command surface for policy management does shift over time (cmdlet parameters and behaviour have changed before and will again) — treat the exact syntax above as a snapshot, and check current Microsoft Learn docs before relying on it for anything beyond a lab.

Sources