Cloud Policy Compliance Dashboard. Real implementation details behind the Azure governance observability platform.
This page documents the live implementation used in the project: custom Azure Policy definitions, management-group initiative assignment, workbook deployment, Log Analytics KQL, alert rule construction, remediation identity and RBAC, and the exact CLI path used to validate detection, alerting, and remediation end to end.
mg-platformrg-governance-core with workspace, workbook, alertsarg("").PolicyResources + policy state filteringallowBlobPublicAccess = false via Azure Policy modifyGitHub / local repo → Bicep deployment → mg-platform policy baseline → workbook + alerts in rg-governance-core → non-compliance detected → alert email → remediation task → storage hardened → policy compliant
Explore the real implementation
These tabs use the same structure as the project build itself: policy definitions and initiative wiring, workbook and KQL layer, alerting logic, remediation configuration, and the CLI validation path that proved the platform worked in practice.
Bicep – management-group baseline deployment
The main management-group template orchestrates the baseline: audit policy, remediation policy, initiative wiring, and assignment. This is the central entry point that turned the project into a reusable governance platform instead of a manual portal build.
targetScope = 'managementGroup'
@description('Management group ID where the governance baseline will be deployed.')
param mgId string
module policyPublicNetworkAccess './policy-public-network-access.bicep' = {
name: 'policy-public-network-access'
scope: managementGroup(mgId)
params: {}
}
module policyRemediateStorage './policy-remediate-storage-network-default-deny.bicep' = {
name: 'policy-remediate-storage-disable-blob-public-access'
scope: managementGroup(mgId)
params: {}
}
module initiativeCloudGovernance './initiative-cloud-governance.bicep' = {
name: 'initiative-cloud-governance'
scope: managementGroup(mgId)
params: {
publicNetworkAuditPolicyDefinitionId: policyPublicNetworkAccess.outputs.policyDefinitionId
publicNetworkRemediationPolicyDefinitionId: policyRemediateStorage.outputs.policyDefinitionId
}
}
module assignmentCloudGovernance './assignment-cloud-governance.bicep' = {
name: 'assignment-cloud-governance'
scope: managementGroup(mgId)
params: {
initiativeDefinitionId: initiativeCloudGovernance.outputs.initiativeDefinitionId
}
}
output policyDefinitionId string = policyPublicNetworkAccess.outputs.policyDefinitionId
output remediationPolicyDefinitionId string = policyRemediateStorage.outputs.policyDefinitionId
output initiativeDefinitionId string = initiativeCloudGovernance.outputs.initiativeDefinitionId
output assignmentId string = assignmentCloudGovernance.outputs.assignmentId
output assignmentPrincipalId string = assignmentCloudGovernance.outputs.assignmentPrincipalId
Bicep – final remediation policy definition
The final working remediation path used Azure Policy modify against Storage Account blob public access. This replaced an earlier remediation approach that did not produce a reliable remediable target.
targetScope = 'managementGroup'
@description('Name of the remediation policy definition.')
param policyName string = 'modify-storage-disable-blob-public-access'
@description('Display name shown in Azure Policy.')
param policyDisplayName string = 'Remediate Storage Accounts to disable blob public access'
@description('Description for the remediation policy.')
param policyDescription string = 'Modifies Storage Accounts so allowBlobPublicAccess is set to false when found non-compliant.'
resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2025-03-01' = {
name: policyName
properties: {
policyType: 'Custom'
mode: 'Indexed'
displayName: policyDisplayName
description: policyDescription
metadata: {
category: 'Storage'
version: '2.0.0'
}
parameters: {}
policyRule: {
if: {
allOf: [
{
field: 'type'
equals: 'Microsoft.Storage/storageAccounts'
}
{
field: 'Microsoft.Storage/storageAccounts/allowBlobPublicAccess'
notEquals: false
}
]
}
then: {
effect: 'modify'
details: {
roleDefinitionIds: [
'/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab'
]
conflictEffect: 'audit'
operations: [
{
operation: 'addOrReplace'
field: 'Microsoft.Storage/storageAccounts/allowBlobPublicAccess'
value: false
}
]
}
}
}
}
}
output policyDefinitionId string = policyDefinition.id
output policyDefinitionName string = policyDefinition.name
Bicep – workbook resource module
The workbook is also deployed as code. That matters because the visual layer stays versioned in the same repo as the policy and alert logic.
targetScope = 'resourceGroup'
@description('Location for the workbook resource.')
param location string
@description('Name of the workbook.')
param workbookDisplayName string = 'Cloud Policy Compliance Dashboard'
@description('Resource ID of the Log Analytics workspace.')
param logAnalyticsWorkspaceId string
@description('Serialized workbook data JSON.')
param workbookData string
resource workbook 'Microsoft.Insights/workbooks@2023-06-01' = {
name: guid(workbookDisplayName, resourceGroup().id)
location: location
kind: 'shared'
properties: {
displayName: workbookDisplayName
sourceId: logAnalyticsWorkspaceId
category: 'workbook'
serializedData: workbookData
}
}
output workbookId string = workbook.id
output workbookName string = workbook.name
Workbook JSON – non-compliance donut and summary table
The workbook was reworked to align with the live query source. The final JSON uses arg("").PolicyResources for policy state data and includes the visual donut/table combination used in the final screenshots.
{
"version": "Notebook/1.0",
"items": [
{
"type": 1,
"content": {
"json": "# Cloud Policy Compliance Dashboard\n\nThis workbook provides a governance-focused view of Azure Policy compliance, non-compliant resources, and policy-related activity across environments."
},
"name": "text-intro"
},
{
"type": 3,
"content": {
"version": "KqlItem/1.0",
"query": "arg(\"\").PolicyResources\n| where type =~ \"microsoft.policyinsights/policystates\"\n| extend complianceState = tostring(properties.complianceState)\n| where complianceState == \"NonCompliant\"\n| extend policyDefinitionName = tostring(properties.policyDefinitionName)\n| summarize nonCompliantResources = count() by policyDefinitionName\n| order by nonCompliantResources desc",
"title": "Non-Compliance by Policy Definition",
"resourceType": "microsoft.operationalinsights/workspaces",
"visualization": "piechart"
},
"name": "noncompliance-donut"
}
]
}
KQL – final policy state query used for workbook and alerts
This was the decisive query change in the build. Using arg("").PolicyResources fixed the workbook and alert path after earlier attempts using the wrong table name failed.
arg("").PolicyResources
| where type =~ "microsoft.policyinsights/policystates"
| extend complianceState = tostring(properties.complianceState)
| where complianceState == "NonCompliant"
KQL files stored in repo
The project kept its operational queries as source files in the repository so that workbook logic and alert logic stayed aligned with the codebase.
PolicyResources
| where type =~ 'Microsoft.PolicyInsights/PolicyStates'
| where tostring(properties.complianceState) == 'NonCompliant'
| extend
assignmentName = tostring(properties.policyAssignmentName),
definitionName = tostring(properties.policyDefinitionName),
initiativeName = tostring(properties.policySetDefinitionName),
resourceId = tostring(properties.resourceId),
resourceType = tostring(properties.resourceType),
resourceLocation = tostring(properties.resourceLocation),
timestamp = todatetime(properties.timestamp)
| summarize nonCompliantResources = count() by assignmentName, initiativeName, definitionName, resourceType, resourceLocation
| order by nonCompliantResources desc
AzureActivity
| where CategoryValue =~ 'Policy'
| where ActivityStatusValue in~ ('Failure', 'Succeeded')
| where OperationNameValue has 'policy'
| extend
resourceId = _ResourceId,
caller = Caller,
operationName = OperationNameValue,
activityStatus = ActivityStatusValue,
subscriptionId = SubscriptionId,
resourceGroup = ResourceGroup,
eventTime = TimeGenerated
| project eventTime, caller, operationName, activityStatus, subscriptionId, resourceGroup, resourceId
| order by eventTime desc
Alerting – non-compliance query and validation path
The production-proof alert path was the non-compliance alert. It fired successfully, showed up in alert history, and delivered an email notification via the action group. The activity-based alert path was explored but not kept as the primary evidence path because the live behavior did not validate consistently enough for the final story.
arg("").PolicyResources
| where type =~ "microsoft.policyinsights/policystates"
| extend complianceState = tostring(properties.complianceState)
| where complianceState == "NonCompliant"
This query crossed a threshold greater than zero and fired the alert-policy-noncompliance rule, which was then visible in alert history and email evidence.
Diagnostic settings – why they mattered
Alert validation also required the subscription Activity Log to be streamed into Log Analytics. Without that, activity investigations and some query experiments had no underlying data to work from.
Name: diag-activity-to-law
Categories selected:
- Administrative
- Policy
- Security
- ServiceHealth
- Recommendation
- ResourceHealth
Destination:
- Send to Log Analytics workspace
- Workspace: law-governance-core
Remediation design – final working pattern
Remediation only became reliable after two changes: the initiative assignment was recreated with a system-assigned managed identity, and the remediation policy was redesigned to target a supported Storage Account property that produced a clean modify-based correction.
ASSIGNMENT_PRINCIPAL_ID="223eac89-e62f-426c-8aea-99c252c3112a"
SUB_ID=$(az account show --query id -o tsv)
az role assignment create \
--assignee-object-id "$ASSIGNMENT_PRINCIPAL_ID" \
--assignee-principal-type ServicePrincipal \
--role Contributor \
--scope /subscriptions/$SUB_ID
Remediation task – management-group execution
The remediation task was created directly against the management-group assignment and the initiative reference ID. The final successful run executed two deployments and returned the environment to compliant state.
az policy remediation create \
--management-group mg-platform \
--name remediate-storage-default-deny \
--policy-assignment /providers/Microsoft.Management/managementGroups/mg-platform/providers/Microsoft.Authorization/policyAssignments/asg-cloud-gov-base \
--definition-reference-id remediateStorageDefaultDeny
{
"deploymentStatus": {
"failedDeployments": 0,
"successfulDeployments": 2,
"totalDeployments": 2
},
"provisioningState": "Succeeded"
}
CLI evidence – deployment sequence used in the live build
These are the exact command patterns used repeatedly through the build to deploy the subscription governance layer, deploy the management-group baseline, and validate outputs.
az deployment sub create \
--name governance-core-sub-deploy \
--location uksouth \
--template-file infra/governance-core-subscription.bicep \
--parameters alertEmailAddress=owusuobed15@yahoo.com
az deployment mg create \
--name mg-platform-baseline-deploy \
--management-group-id mg-platform \
--location uksouth \
--template-file mg/main-mg-platform.bicep \
--parameters mgId=mg-platform
CLI evidence – final verification commands
These commands were the final proof that the platform completed the remediation story and restored compliance.
az storage account show \
--name stnoncompliance7348 \
--resource-group rg-governance-core \
--query "allowBlobPublicAccess" \
-o tsv
az policy state list \
--resource-group rg-governance-core \
--query "[?contains(resourceId, 'stnoncompliance7348')].{definition:policyDefinitionName, referenceId:policyDefinitionReferenceId, compliance:complianceState}" \
-o table
false
Definition ReferenceId Compliance
----------------------------------------- ------------------------------- ------------
modify-storage-disable-blob-public-access remediatestoragedefaultdeny Compliant
audit-storage-public-network-access auditstoragepublicnetworkaccess Compliant
modify-storage-network-default-deny remediatestoragedefaultdeny Compliant
CLI evidence screenshots included in package
The project package also includes rendered terminal screenshots for the final remediation run and the final verification output so the website can show the live command evidence visually.