Blog Post

Azure Infrastructure Blog
4 MIN READ

Reimagining Azure Governance with Automation & EPAC

sameenamohammed's avatar
May 02, 2026

Managing Azure governance across multiple subscriptions can quickly become overwhelming. As environments grow, enforcing policies manually leads to inconsistencies, delays, and operational risk. This blog shares a practical, hands-on perspective — not just what EPAC is, but how we implemented it, challenges faced, and lessons learned.

đź§© The Challenge: Governance at Scale

Managing Azure environments manually introduces:

  • ❌ Policy drift across subscriptions
  • ❌ Inconsistent naming conventions
  • ❌ Delays in compliance enforcement
  • ❌ Human errors in deployments

🔍 Insight: Governance gaps are often not due to lack of policies—but lack of automation.

Solution Overview: EPAC

Enterprise Policy as Code (EPAC) to bring governance into the DevOps workflow.

EPAC helped us:

  • Treat policies like code
  • Automate deployments
  • Standardize governance
  • Maintain audit history

Architecture Overview

Our EPAC setup included:

  • Management Groups hierarchy for governance scope
  • Policy Definitions & Initiatives (JSON/Bicep)
  • Azure DevOps Pipeline for deployment
  • Managed Identity for secure execution
This is sample implementation flow of EPAC in enterprises

Flow Explanation

  1. Azure DevOps Pipeline
    • Triggers CI/CD process
    • Executes deployment scripts
    • Authenticates securely using Managed Identity
  2. EPAC Framework
    • Stores policies as code
    • Enables version control and validation
    • Acts as the central governance engine
  3. Azure Policy Engine
    • Evaluates resources against defined policies
    • Enforces compliance automatically
  4. Target Environments
    • Policies applied across:
      • Management Groups
      • Subscriptions
      • Resource Groups

Why Policy-as-Code Matters

âś… Standardized governance across environments
âś… Faster onboarding of subscriptions
âś… Improved audit readiness
âś… Repeatable and reliable policy deployment
âś… Seamless DevOps integration

Security & Compliance Benefits

  • Enforces least privilege access
  • Prevents misconfigured deployments
  • Supports continuous compliance
  • Aligns with standards like ISO 27001

Implementation Walkthrough

âś… Step 1: Structuring Policy Repository

This structure ensured:

  • Clear separation of concerns
  • Reusability
  • Easy onboarding for new contributors

âś… Step 2: Defining Policies

We created custom policies and reused built-in ones.

Example: Restrict allowed regions

{

  "if": {

    "field": "location",

    "notIn": ["eastus", "westeurope"]

  },

  "then": {

    "effect": "deny"

  }

}

âś… Step 3: Creating Initiatives

Instead of assigning individual policies, we grouped them into initiatives:

  • Security baseline
  • Tagging compliance
  • Cost optimization

👉 This reduced duplication and simplified assignments.

âś… Step 4: Pipeline Automation

We built an Azure DevOps pipeline to:

  • Validate policy templates
  • Deploy definitions to management groups
  • Assign initiatives automatically

Example pipeline flow:

This is example pipeline structure for deploying epac policies targeted to single environment 

parameters:
  - name: forceDeployment
    displayName: 'Force deployment (ignore change detection)'
    type: boolean
    default: false
  - name: clearAgentCache
    displayName: 'Clear agent container cache (recommended for troubleshooting)'
    type: boolean
    default: true

variables:
  # Pipeline per il deployment delle Policy in ambiente Canary/Test
  PAC_OUTPUT_FOLDER: ./Output
  PAC_DEFINITIONS_FOLDER: ./Definitions

  # Service connection per l'ambiente di test/canary
  serviceConnection: "SC-EPAC-CONTRIBUTOR-TST-001"

  # Environment selector per canary
  pacEnvironmentSelector: canary

# Trigger: deploy solo manualmente o da branch specifici
trigger: none

# PR trigger per validazione
pr:
  branches:
    include:
    - main
    - feature/*
  paths:
    include:
    - src/IaC/Infrastructure/epac/Definitions/*

pool:
  name: "TST-AgentPool-01"

stages:
  - stage: Plan
    displayName: "Plan Canary Environment"
    jobs:
      - job: Plan
        displayName: "Generate Deployment Plan"
        steps:
          - template: templates/plan.yml
            parameters:
              serviceConnection: $(serviceConnection)
              pacEnvironmentSelector: ${{ variables.pacEnvironmentSelector }}

  - stage: Deploy
    displayName: "Deploy to Canary (Audit Mode)"
    dependsOn: Plan
    condition: and(not(failed()), not(canceled()), or(eq('${{ parameters.forceDeployment }}', 'true'), and(eq('${{ parameters.forceDeployment }}', 'false'), or(eq(dependencies.Plan.outputs['Plan.Plan.deployPolicyChanges'], 'yes'), eq(dependencies.Plan.outputs['Plan.Plan.deployRoleChanges'], 'yes')))))
    variables:
      PAC_INPUT_FOLDER: "$(Pipeline.Workspace)/plans-${{ variables.pacEnvironmentSelector }}"
      localDeployPolicyChanges: $[stageDependencies.Plan.Plan.outputs['Plan.deployPolicyChanges']]
      localDeployRoleChanges: $[stageDependencies.Plan.Plan.outputs['Plan.deployRoleChanges']]
    jobs:
      - deployment: DeployPolicy
        displayName: "Deploy Policy Changes (Audit Mode)"
        environment: PAC-CANARY
        condition: and(not(failed()), not(canceled()), or(eq('${{ parameters.forceDeployment }}', 'true'), and(eq('${{ parameters.forceDeployment }}', 'false'), eq(variables.localDeployPolicyChanges, 'yes'))))
        strategy:
          runOnce:
            deploy:
              steps:
                - template: templates/deploy-policy.yml
                  parameters:
                    serviceConnection: $(serviceConnection)
                    pacEnvironmentSelector: ${{ variables.pacEnvironmentSelector }}
                    forceDeployment: ${{ parameters.forceDeployment }}
                
      - deployment: DeployRoles
        displayName: "Deploy Role Assignments"
        dependsOn: DeployPolicy
        environment: PAC-CANARY
        condition: and(not(failed()), not(canceled()), eq(variables.localDeployRoleChanges, 'yes'))  # Riabilitato per AMBA managed identity
        strategy:
          runOnce:
            deploy:
              steps:
                - template: templates/deploy-roles.yml
                  parameters:
                    serviceConnection: $(serviceConnection)
                    pacEnvironmentSelector: ${{ variables.pacEnvironmentSelector }}

  # Stage opzionale per validazione post-deployment
  - stage: Validate
    displayName: "Validate Canary Deployment"
    dependsOn: Deploy
    condition: and(succeeded(), ne(variables['Build.Reason'], 'PullRequest'))
    jobs:
      - job: ValidateCompliance
        displayName: "Validate Policy Compliance"
        steps:
          - task: PowerShell@2
            displayName: "Check Policy Compliance Status"
            inputs:
              targetType: 'inline'
              script: |
                Write-Host "##[section]Canary deployment completed successfully"
                Write-Host "##[warning]Remember: All policies are in AUDIT mode - monitor compliance dashboard"
                Write-Host "##[task.complete result=Succeeded;]Canary validation completed"

 

âś… Step 5: Secure Deployment using Managed Identity

We used:

  • System-assigned Managed Identity
  • RBAC roles (Policy Contributor / Reader)

âś… Benefit:

  • No secrets in pipeline
  • Improved security posture

âś… Step 6: Policy Assignment at Scale

Policies were assigned at:

  • Root Management Group
  • Subscription level (when needed)

This ensured:

  • Consistent enforcement
  • Centralized control

Real Use Cases Implemented

Using EPAC, we solved real scenarios:

  • 🔹 Enforcing naming conventions
  • 🔹 Ensuring mandatory resource tagging
  • 🔹 Restricting deployment regions
  • 🔹 Enforcing backup policies on disks
  • 🔹 Preventing creation of non-compliant resources

Best Practices

  • Start with baseline policies first
  • Use initiatives instead of individual assignments
  • Enable PR-based approvals
  • Always test policies in lower environments
  • Maintain clear documentation

Conclusion

Implementing EPAC transformed our governance model from manual and reactive → automated and proactive.

For teams managing complex Azure environments, EPAC provides:

  • Scalability
  • Consistency
  • Security

If you are still managing policies manually, this is the right time to: 👉 Move to Policy as Code

Updated May 02, 2026
Version 1.0
No CommentsBe the first to comment