Skip to content

Azure Cost Governance & Policies — Complete Guide

DodaTech Updated 2026-06-20 7 min read

In this tutorial, you'll learn about Azure Cost Governance & Policies. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.

Azure cost governance is the discipline of enforcing spending rules across subscriptions using Azure Policy, management groups, RBAC, and mandatory tagging — preventing resource sprawl before it reaches the bill.

What You'll Learn

You'll implement Azure Policy to enforce cost rules, design a management group hierarchy for budget isolation, apply RBAC to restrict expensive resource creation, and build a tagging governance framework that ensures every resource is accountable to a cost center.

Why It Matters

Without governance, developers provision GPU VMs for testing, leave load balancers running over weekends, and spin up resources in expensive regions — all without oversight. Azure Policy and management groups prevent this at the subscription level. DodaTech reduced non-production spend by 35% across 12 subscriptions by enforcing Deny policies on expensive SKUs for dev environments.

flowchart TD
    A[Management Group Root] --> B[Production MG]
    A --> C[Non-Production MG]
    A --> D[Sandbox MG]
    B --> E[Azure Policy: Allowed SKUs]
    B --> F[Policy: Required Tags]
    C --> G[Policy: Deny GPU / High-Mem]
    C --> H[Policy: Enforce Dev/Test Pricing]
    D --> I[Policy: Budget Limit $500]
    D --> J[Policy: Auto-Shutdown 7PM-7AM]
    E --> K[Compliant Spend]
    style K fill:#22c55e,color:#fff

1. Management Group Hierarchy

Management groups let you apply policies and budgets at scale across hundreds of subscriptions.

# Create management group structure
az account management-group create --name "dodatech-root" --display-name "DodaTech Root"
az account management-group create --name "production" --display-name "Production" --parent "dodatech-root"
az account management-group create --name "non-prod" --display-name "Non-Production" --parent "dodatech-root"
az account management-group create --name "sandbox" --display-name "Sandbox" --parent "dodatech-root"

# Move a subscription into a management group
az account management-group subscription add \
  --name "production" \
  --subscription "sub-12345678-1234-1234-1234-123456789012"

# List hierarchy
az account management-group list --query "[].{Name:name, DisplayName:displayName, Children:children}"

Expected output:

[
    {"Name": "dodatech-root", "DisplayName": "DodaTech Root", "Children": ["production", "non-prod", "sandbox"]},
    {"Name": "production", "DisplayName": "Production", "Children": []},
    {"Name": "non-prod", "DisplayName": "Non-Production", "Children": []},
    {"Name": "sandbox", "DisplayName": "Sandbox", "Children": []}
]

2. Azure Policy for Cost Control

Azure Policy enforces rules on resources at creation time. Cost-related policies prevent expensive resources in environments where they don't belong.

# Create a policy to deny expensive VM SKUs in non-production
az policy definition create \
  --name "deny-expensive-skus-nonprod" \
  --display-name "Deny expensive VM SKUs in non-prod" \
  --description "Prevent GPU, memory-optimized, and high-CPU VMs in non-production subscriptions" \
  --rules '{
    "if": {
      "allOf": [
        {
          "field": "type",
          "equals": "Microsoft.Compute/virtualMachines]
        },
        {
          "field": "Microsoft.Compute/virtualMachines/sku.name",
          "in": ["Standard_NC6", "Standard_NC12", "Standard_NC24", "Standard_E64s_v3", "Standard_E80is_v4"]
        }
      ]
    },
    "then": {"effect": "Deny"}
  }'

# Assign policy to non-production management group
az policy assignment create \
  --name "deny-expensive-skus-nonprod-assignment" \
  --policy "deny-expensive-skus-nonprod" \
  --scope "/providers/Microsoft.Management/managementGroups/non-prod"

# Create a policy to require specific tags
az policy definition create \
  --name "require-cost-tags" \
  --display-name "Require cost allocation tags" \
  --rules '{
    "if": {
      "field": "tags",
      "exists": "false"
    },
    "then": {"effect": "deny"}
  }' \
  --params '{
    "tagNames": {
      "type": "Array",
      "metadata": {"displayName": "Required Tags"},
      "allowedValues": ["CostCenter", "Environment", "Project", "Owner"]
    }
  }'

Expected output:

{
    "name": "deny-expensive-skus-nonprod",
    "properties": {
        "policyType": "Custom",
        "mode": "All",
        "displayName": "Deny expensive VM SKUs in non-prod"
    }
}

3. RBAC for Cost Management

Use Azure RBAC to control who can create, modify, or delete resources and manage budgets.

# Create a custom role for cost viewers
az role definition create \
  --role-definition '{
    "Name": "Cost Reader",
    "Description": "View costs and budgets but cannot modify resources",
    "Actions": [
      "Microsoft.Consumption/*/read",
      "Microsoft.Billing/*/read",
      "Microsoft.Management/*/read]
    ],
    "DataActions": [],
    "NotActions": [],
    "AssignableScopes": ["/subscriptions/sub-12345678-1234-1234-1234-123456789012"]
  }'

# Assign Cost Reader role to a user
az role assignment create \
  --assignee "finops-user@dodatech.com" \
  --role "Cost Reader" \
  --scope "/subscriptions/sub-12345678-1234-1234-1234-123456789012"

# Restrict who can create expensive resources
az role definition create \
  --role-definition '{
    "Name": "Limited VM Creator",
    "Description": "Can only create VMs under a specific cost threshold",
    "Actions": [
      "Microsoft.Compute/virtualMachines/write",
      "Microsoft.Network/*/write]
    ],
    "NotActions": [],
    "AssignableScopes": ["/subscriptions/sub-12345678-1234-1234-1234-123456789012/resourceGroups/dev-rg"]
  }'

4. Tagging Governance Strategy

Enforce a standard tag taxonomy across all resources for cost allocation and chargeback.

# Apply tags to a resource group via policy
az policy definition create \
  --name "inherit-resourcegroup-tags" \
  --display-name "Inherit resource group tags on new resources" \
  --rules '{
    "if": {
      "field": "type",
      "in": ["Microsoft.Compute/virtualMachines", "Microsoft.Storage/storageAccounts", "Microsoft.Sql/servers/databases"]
    },
    "then": {
      "effect": "modify",
      "details": {
        "roleDefinitionIds": ["/providers/microsoft.authorization/roleDefinitions/b24988ac-6180-42a0-ab88-20f7382dd24c"],
        "operations": [
          {
            "operation": "addOrReplace",
            "field": "tags[\"CostCenter\"]",
            "value": "[resourceGroup().tags[\"CostCenter\"]]"
          }
        ]
      }
    }
  }'

Mandatory tag taxonomy:

  • CostCenter: cc-1001, cc-1002, cc-1003
  • Environment: prod, staging, dev, test, sandbox
  • Project: web-app, data-pipeline, ml-training
  • Owner: email alias of team responsible
  • AutoShutdown: true, false (enforced on non-prod)

5. Budget Governance with Action Groups

Automate responses to budget breaches using Azure Action Groups.

# Create an action group for cost alerts
az monitor action-group create \
  --name "cost-alerts" \
  --resource-group "management-rg" \
  --action email finops "finops@example.com" \
  --action email cto "cto@example.com" \
  --action azure-function auto-remediate "func-cost-remediation.azurewebsites.net"

# Create a budget with automated actions
az consumption budget create \
  --budget-name "monthly-prod" \
  --category cost \
  --amount 50000 \
  --time-grain monthly \
  --scope "/subscriptions/sub-12345678-1234-1234-1234-123456789012" \
  --start-date 2026-01-01 \
  --end-date 2026-12-31 \
  --notifications '{
    "Actual_GreaterThan_80": {
      "enabled": true,
      "operator": "GreaterThan",
      "threshold": 80,
      "contact-emails": ["finops"@example".com"],
      "contact-roles": ["Owner"],
      "contact-groups": ["/subscriptions/sub-12345678-1234-1234-1234-123456789012/resourceGroups/management-rg/providers/microsoft.insights/actionGroups/cost-alerts"]
    }
  }'

Common Mistakes

  1. Policies without exclusions: A blanket Deny policy can block legitimate production deployments. Always add exclusion scopes for emergency break-glass scenarios.

  2. No management group hierarchy: Applying policies to individual subscriptions is unsustainable at scale. Use management groups for inheritance.

  3. Tags without enforcement: Optional tags are ignored. Use Azure Policy with deny or modify effects to enforce mandatory tags at resource creation.

  4. RBAC too permissive: The Contributor role can create any resource. Scope custom roles to specific resource types and SKUs for cost-sensitive environments.

  5. Ignoring sandbox subscriptions: Without budget limits, sandbox subscriptions can run up costs. Enforce strict budgets and auto-shutdown policies on sandboxes.

Practice Questions

  1. How does Azure Policy prevent cost overruns before they happen? Answer: Azure Policy evaluates resources at creation time and can Deny or Modify them based on rules — preventing expensive SKUs, enforcing tags, and blocking resources in unapproved regions before they incur cost.

  2. What is the difference between a management group and a subscription in cost governance? Answer: Management groups are containers for organizing subscriptions. Policies and budgets applied at the management group level are inherited by all child subscriptions, enabling governance at scale.

  3. How do you enforce mandatory cost allocation tags across all Azure resources? Answer: Create a custom Azure Policy definition with effect deny that requires specific tags (CostCenter, Environment, Owner) on all resources. Assign it at the root management group.

Challenge

Design a cost governance framework for a 50-subscription Azure enterprise: create a three-level management group hierarchy (Production, Non-Production, Sandbox), write Azure Policy definitions that deny GPU SKUs in non-production and require CostCenter tags, assign the Cost Reader role to each team's FinOps lead, configure budget alerts at 80/95/100% with Action Groups, and implement auto-shutdown policies for all non-production VMs between 7 PM and 7 AM.

FAQ

Can Azure Policy block existing non-compliant resources?

: Out of the box, Azure Policy only blocks new resources. Use DeployIfNotExists or Modify effects with remediation tasks to fix existing non-compliant resources.

What is the cost governance hierarchy in Azure?

: Root Management Group -> Management Groups -> Subscriptions -> Resource Groups -> Resources. Policies inherit downward, so apply broad rules at higher levels.

How do I exclude emergency resources from cost policies?

: Add exclusion scopes (specific resource groups or subscriptions) to your policy assignment. Use a dedicated "break-glass" subscription for emergency access.

Can I automate VM shutdown based on budget alerts?

: Yes. Use Azure Budgets with Action Groups that trigger Azure Functions or Automation Runbooks to stop VMs when a budget threshold is breached.

How often do Azure Policy assignments take effect?

: Policy assignments take effect within 30 minutes. New resource creation is evaluated immediately; existing resources are evaluated during periodic Compliance scans (every 24 hours).

What's Next

Topic Description
{{< card link="../azure-cost-management" title="Azure Cost Management" icon="currency-dollar" >}} Full Azure cost optimization guide
{{< card link="../tagging-labeling-strategy" title="Tagging & Labeling Strategy" icon="tag" >}} Cloud resource tagging best practices

Related topics: Cloud Cost Optimization, Azure, DevOps

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro