Cloud Policy Compliance Dashboard - Engineering Deep Dive.
How one audit requirement grew into a full Azure governance baseline: mg-platform policies and initiatives defined in Bicep, centralized deployments through GitHub Actions, a shared rg-governance-core pattern per subscription and an mg-level telemetry pipeline powering Workbooks and alerts across Dev/Test/Prod.
GitHub repo → GitHub Actions CI/CD (Build · Validate · Deploy to Dev/Test/Prod)
→ Cloud-Governance-Baseline initiative at mg-platform →
rg-governance-core in each subscription →
policy & activity logs into Log Analytics → Workbook dashboard → alerts and notifications.
From mg-platform policies to Workbook insights and alerts..
This section walks through the exact engineering artefacts shown in the architecture: mg-level Bicep modules for policies and the Cloud-Governance-Baseline initiative, mg-platform assignment, the rg-governance-core pattern deployedto each subscription (Dev/Test/Prod), the three-stage GitHub Actions pipeline, and the Workbook + KQL queries that surface policy signals into dashboards and alerts.
Bicep Cloud Governance Baseline at mg-platform.
The governance baseline is defined at the management group level, not just a single
subscription. A custom Storage network policy is created and then wrapped in the
Cloud-Governance-Baseline initiative at mg-platform, ready to grow with
additional controls: NSG hygiene, tag enforcement, security policies, and “diagnostic settings
required” rules for core resources.
targetScope = 'managementGroup'
@description('Name of the management group where the baseline will live.')
param managementGroupName string = 'mg-platform'
@description('Display name for the custom Storage network policy.')
param policyDisplayName string = 'Audit public network access on Storage Accounts'
@description('Policy definition category.')
param policyCategory string = 'Network'
resource policy 'Microsoft.Authorization/policyDefinitions@2022-06-01' = {
name: 'audit-public-network-access-storage'
properties: {
displayName: policyDisplayName
mode: 'Indexed'
policyType: 'Custom'
metadata: {
category: policyCategory
}
policyRule: {
if: {
allOf: [
{
field: 'type'
equals: 'Microsoft.Storage/storageAccounts'
}
{
field: 'Microsoft.Storage/storageAccounts/networkAcls.defaultAction'
equals: 'Allow'
}
]
}
then: {
effect: 'audit'
}
}
}
}
output policyDefinitionId string = policy.id
targetScope = 'managementGroup'
@description('Management group for the Cloud Governance baseline.')
param managementGroupName string = 'mg-platform'
@description('Display name for the Cloud Governance baseline initiative.')
param initiativeDisplayName string = 'Cloud-Governance-Baseline'
@description('ID of the custom policy definition to include in the initiative.')
param policyDefinitionId string
resource initiative 'Microsoft.Authorization/policySetDefinitions@2022-06-01' = {
name: 'cloud-governance-baseline'
properties: {
displayName: initiativeDisplayName
policyType: 'Custom'
metadata: {
category: 'Cloud Governance'
}
parameters: {}
policyDefinitions: [
{
policyDefinitionId: policyDefinitionId
parameters: {}
}
// Future: NSG hygiene, tag enforcement, security policies, diagnostic settings
]
}
}
output initiativeId string = initiative.id
mg-platform assignment & rg-governance-core per subscription.
To match the diagram, the initiative is assigned at the mg-platform management group.
Every subscription under mg-platform (sub-dev, sub-test, sub-prod) inherits the baseline.
Separately, a subscription-scoped Bicep template creates rg-governance-core in each
subscription with a Log Analytics workspace, Workbook, alert rules, Action Group and optional
remediation Logic App.
targetScope = 'managementGroup'
@description('Management group that will host the Cloud Governance baseline.')
param managementGroupName string = 'mg-platform'
@description('Display name for the initiative assignment.')
param assignmentDisplayName string = 'Cloud-Governance-Baseline-mg-platform'
@description('ID of the initiative to assign.')
param initiativeId string
resource mgAssignment 'Microsoft.Authorization/policyAssignments@2022-06-01' = {
name: 'cloud-governance-baseline-mg-platform'
scope: managementGroupResourceId(managementGroupName)
properties: {
displayName: assignmentDisplayName
policyDefinitionId: initiativeId
enforcementMode: 'Default'
parameters: {}
}
}
targetScope = 'subscription'
@description('Location for governance resources.')
param location string = 'westeurope'
@description('Name of the governance resource group.')
param governanceRgName string = 'rg-governance-core'
@description('Email address for the governance Action Group.')
param governanceEmail string
resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' = {
name: governanceRgName
location: location
}
resource workspace 'Microsoft.OperationalInsights/workspaces@2022-10-01' = {
name: 'law-governance'
location: location
kind: 'Workspace'
sku: {
name: 'PerGB2018'
}
properties: {
retentionInDays: 30
}
}
resource actionGroup 'Microsoft.Insights/actionGroups@2022-06-15' = {
name: 'ag-cloud-policy-deny'
location: 'global'
properties: {
groupShortName: 'GovDeny'
enabled: true
emailReceivers: [
{
name: 'PlatformTeam'
emailAddress: governanceEmail
useCommonAlertSchema: true
}
]
}
}
// Optional: Logic App for remediation, Workbook deployment & alert rules are added as separate modules
targetScope = 'managementGroup'
@description('Management group name for the governance baseline.')
param managementGroupName string = 'mg-platform'
module policy './policy-public-network-access.bicep' = {
name: 'policy-storage-public-network'
params: {
managementGroupName: managementGroupName
}
}
module initiative './initiative-cloud-governance.bicep' = {
name: 'cloud-governance-initiative'
params: {
managementGroupName: managementGroupName
policyDefinitionId: policy.outputs.policyDefinitionId
}
}
module assignment './assignment-cloud-governance.bicep' = {
name: 'cloud-governance-assignment'
params: {
managementGroupName: managementGroupName
initiativeId: initiative.outputs.initiativeId
}
}
CI/CD – multi-stage GitHub Actions across Dev, Test, Prod.
The CI/CD pipeline mirrors the left side of the architecture diagram: a developer pushes to
main, a GitHub Actions workflow builds and validates the Bicep templates,
then deploys to three environments – sub-dev, sub-test and
sub-prod. The management group baseline is deployed once to
mg-platform, while the rg-governance-core resources and Workbook
are deployed into each subscription.
name: deploy-cloud-policy-dashboard
on:
push:
branches: [ "main" ]
workflow_dispatch: {}
permissions:
id-token: write
contents: read
env:
AZURE_TENANT_ID: ${{ secrets.AZURE_TENANT_ID }}
AZURE_CLIENT_ID: ${{ secrets.AZURE_CLIENT_ID }}
MG_PLATFORM: "mg-platform"
SUB_DEV: ${{ secrets.AZURE_SUB_DEV_ID }}
SUB_TEST: ${{ secrets.AZURE_SUB_TEST_ID }}
SUB_PROD: ${{ secrets.AZURE_SUB_PROD_ID }}
jobs:
build-and-validate:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Azure login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ env.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.SUB_DEV }} # any subscription for validation
- name: Install Bicep CLI
run: |
az bicep install
az bicep version
- name: Validate mg-platform baseline (what-if)
run: |
az deployment mg what-if \
--name mg-platform-whatif \
--location westeurope \
--management-group-id $MG_PLATFORM \
--template-file ./mg/main-mg-platform.bicep
- name: Validate governance-core for subscriptions (what-if dev)
run: |
az account set --subscription $SUB_DEV
az deployment sub what-if \
--name gov-core-dev-whatif \
--location westeurope \
--template-file ./infra/governance-core-subscription.bicep \
--parameters governanceEmail='platform-team@contoso.com'
deploy-mg-platform:
runs-on: ubuntu-latest
needs: build-and-validate
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Azure login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ env.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ env.SUB_DEV }} # any sub; mg deployments ignore subscription
- name: Deploy Cloud-Governance-Baseline to mg-platform
run: |
az deployment mg create \
--name mg-platform-governance \
--location westeurope \
--management-group-id $MG_PLATFORM \
--template-file ./mg/main-mg-platform.bicep
deploy-subscriptions:
runs-on: ubuntu-latest
needs: deploy-mg-platform
strategy:
matrix:
env:
- name: dev
sub: ${{ env.SUB_DEV }}
- name: test
sub: ${{ env.SUB_TEST }}
- name: prod
sub: ${{ env.SUB_PROD }}
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Azure login (OIDC)
uses: azure/login@v2
with:
client-id: ${{ env.AZURE_CLIENT_ID }}
tenant-id: ${{ env.AZURE_TENANT_ID }}
subscription-id: ${{ matrix.env.sub }}
- name: Deploy rg-governance-core to ${{ matrix.env.name }}
run: |
az deployment sub create \
--name gov-core-${{ matrix.env.name }} \
--location westeurope \
--template-file ./infra/governance-core-subscription.bicep \
--parameters governanceEmail='platform-team@contoso.com'
This matches the diagram: a single pipeline builds and validates the templates, deploys the management group baseline once, then rolls the governance core out to Dev, Test and Prod subscriptions.
KQL queries & Workbook JSON – the governance scoreboard.
The right-hand column of the diagram shows the telemetry flow. Policy evaluation and
DENY events surface in AzureActivity and policy resource tables.
The Workbook running in rg-governance-core turns those signals into a governance
scoreboard for mg-platform: donuts by policy, trend lines over time, and drill-down tables that
show which subscriptions and resource groups are the noisiest.
AzureActivity
| where CategoryValue == "Policy"
| where ActivityStatusValue == "Failure"
| where OperationNameValue contains "deny" or OperationNameValue contains "DENY"
| project TimeGenerated,
SubscriptionId,
ResourceGroup = tostring(ResourceGroup),
Caller,
OperationNameValue,
ActivityStatusValue
| summarize DenyCount = count() by bin(TimeGenerated, 15m), SubscriptionId, ResourceGroup, Caller
| order by TimeGenerated desc
PolicyResources
| where isnotempty(ComplianceState)
| summarize
NonCompliantResources = countif(ComplianceState == "NonCompliant"),
CompliantResources = countif(ComplianceState == "Compliant")
by PolicyDefinitionName = tostring(PolicyDefinitionName),
SubscriptionId
| order by NonCompliantResources desc
targetScope = 'resourceGroup'
@description('Name of the Workbook.')
param workbookName string = 'Cloud Policy Compliance Dashboard'
@description('Log Analytics workspace resource ID.')
param workspaceResourceId string
@description('Location for the Workbook.')
param location string
@description('Exported workbook JSON (trimmed for brevity).')
@secure()
param workbookJson string
resource workbook 'microsoft.insights/workbooks@2022-04-01' = {
name: guid(workbookName)
location: location
kind: 'shared'
properties: {
displayName: workbookName
serializedData: workbookJson
sourceId: workspaceResourceId
category: 'workbook'
}
}
// The exported JSON defines tiles that mirror the diagram:
// - Donut: Non-compliant resources by policy
// - Column chart: DENY events over time
// - Table: Drill-down by subscription, RG, caller
Testing & validation – proving the mg-platform baseline works.
To align with the diagram, I validated the baseline from mg-platform down into each subscription: create misconfigurations in sub-dev, watch them surface in the shared Workbook, and confirm that alerts fire via the Action Group. The same pattern applies to sub-test and sub-prod, ensuring that governance behaves consistently before anything touches production.
// 1. In sub-dev, create a Storage Account with default network access = Allow.
// 2. Wait for Azure Policy evaluation (Cloud-Governance-Baseline at mg-platform).
// 3. Confirm non-compliance + DENY events in AzureActivity and PolicyResources.
// 4. Validate Workbook donut/table + alert firing to the Action Group.
AzureActivity
| where CategoryValue == "Policy"
| where SubscriptionId == ""
| where OperationNameValue contains "StorageAccounts"
| project TimeGenerated, Caller, OperationNameValue, ResourceGroup
| order by TimeGenerated desc
// Example KQL once an NSG hygiene policy is added to the initiative.
// Checks for wide-open inbound NSG rules (0.0.0.0/0) on admin ports
// across all subscriptions under mg-platform.
AzureNetworkAnalytics_CL
| where Direction_s == "Inbound"
| where RemoteIP_s == "0.0.0.0/0"
| where DstPort_d in (22, 3389)
| summarize OpenRules = count() by NSG = tostring(SecurityRule_s), SubscriptionId
- [x] mg-platform baseline deploys without errors (what-if + create).
- [x] Cloud-Governance-Baseline appears at mg-platform with expected policy definitions.
- [x] sub-dev, sub-test, sub-prod all inherit the mg-platform assignment.
- [x] rg-governance-core is deployed in each subscription with workspace & Action Group.
- [x] Non-compliant resources appear in Policy blade and Workbook for each sub.
- [x] DENY events are written to AzureActivity and visible in Log Analytics.
- [x] Alert rule fires for DENY events in each environment.
- [x] Emails from the Action Group land in the governance mailbox.
- [x] After remediation, compliance improves and alerts stop firing.
This gives mg-platform a repeatable way to prove that the baseline works in all three environments before onboarding additional subscriptions or adding more policies (NSG, Key Vault, tags, diagnostics).