Introduction: Governing the Multi-Cloud Enterprise
Most enterprises run workloads across Azure, AWS, on-premises data centers, and edge locations. Without unified governance, each environment becomes a silo with its own compliance gaps, cost overruns, and security blind spots. This deep dive builds a comprehensive governance platform using Azure as the control plane — extending Azure Arc to manage non-Azure resources, enforcing policies everywhere, standardizing deployments with Landing Zones, and providing a single pane of glass for cost visibility and optimization.

Prerequisites
- Azure subscription with Owner access at Management Group level
- Azure Arc-enabled servers agent access for on-premises/AWS VMs
- AWS account with IAM permissions for Arc onboarding
- Microsoft Entra ID Premium P2
- Familiarity with Azure Resource Manager, Bicep, and Policy-as-Code
Phase 1: Azure Landing Zone Foundation
Management Group Hierarchy
targetScope = 'managementGroup'
// Root Management Group hierarchy
resource rootMg 'Microsoft.Management/managementGroups@2021-04-01' = {
name: 'mg-contoso-root'
properties: {
displayName: 'Contoso Enterprise'
}
}
resource platformMg 'Microsoft.Management/managementGroups@2021-04-01' = {
name: 'mg-platform'
properties: {
displayName: 'Platform'
details: { parent: { id: rootMg.id } }
}
}
resource landingZonesMg 'Microsoft.Management/managementGroups@2021-04-01' = {
name: 'mg-landing-zones'
properties: {
displayName: 'Landing Zones'
details: { parent: { id: rootMg.id } }
}
}
resource sandboxMg 'Microsoft.Management/managementGroups@2021-04-01' = {
name: 'mg-sandbox'
properties: {
displayName: 'Sandbox'
details: { parent: { id: rootMg.id } }
}
}
// Landing Zone sub-groups
var landingZoneTypes = ['corp', 'online', 'confidential']
resource lzSubGroups 'Microsoft.Management/managementGroups@2021-04-01' = [for lz in landingZoneTypes: {
name: 'mg-lz-${lz}'
properties: {
displayName: 'LZ - ${toUpper(lz)}'
details: { parent: { id: landingZonesMg.id } }
}
}]
// Platform subscriptions
resource identitySub 'Microsoft.Management/managementGroups/subscriptions@2021-04-01' = {
parent: platformMg
name: 'identity-subscription-id'
}
resource connectivitySub 'Microsoft.Management/managementGroups/subscriptions@2021-04-01' = {
parent: platformMg
name: 'connectivity-subscription-id'
}
resource managementSub 'Microsoft.Management/managementGroups/subscriptions@2021-04-01' = {
parent: platformMg
name: 'management-subscription-id'
}
Landing Zone Subscription Vending
// modules/subscription-vending.bicep
targetScope = 'managementGroup'
@description('Landing zone request parameters')
param lzRequest object = {
workloadName: ''
environment: '' // dev, staging, prod
costCenter: ''
businessUnit: ''
dataClassification: '' // public, internal, confidential
owner: ''
}
// Create subscription (using Subscription Alias API)
resource subscription 'Microsoft.Subscription/aliases@2021-10-01' = {
name: '${lzRequest.workloadName}-${lzRequest.environment}'
properties: {
displayName: '${lzRequest.businessUnit}-${lzRequest.workloadName}-${lzRequest.environment}'
billingScope: '/billingAccounts/${billingAccountId}/enrollmentAccounts/${enrollmentAccountId}'
workload: lzRequest.environment == 'prod' ? 'Production' : 'DevTest'
additionalProperties: {
managementGroupId: '/providers/Microsoft.Management/managementGroups/mg-lz-${lzRequest.dataClassification == 'confidential' ? 'confidential' : 'corp'}'
tags: {
CostCenter: lzRequest.costCenter
BusinessUnit: lzRequest.businessUnit
Environment: lzRequest.environment
DataClassification: lzRequest.dataClassification
Owner: lzRequest.owner
ManagedBy: 'LandingZone-Automation'
}
}
}
}

Phase 2: Azure Policy-as-Code
Enterprise Policy Definitions
{
"properties": {
"displayName": "Enforce encryption at rest for all storage accounts",
"description": "Ensures all storage accounts use customer-managed keys with Key Vault",
"mode": "Indexed",
"metadata": {
"category": "Encryption",
"version": "2.0.0"
},
"parameters": {
"effect": {
"type": "String",
"defaultValue": "Deny",
"allowedValues": ["Audit", "Deny", "Disabled"]
},
"minimumTlsVersion": {
"type": "String",
"defaultValue": "TLS1_2"
}
},
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Storage/storageAccounts"
},
{
"anyOf": [
{
"field": "Microsoft.Storage/storageAccounts/encryption.keySource",
"notEquals": "Microsoft.Keyvault"
},
{
"field": "Microsoft.Storage/storageAccounts/minimumTlsVersion",
"notEquals": "[parameters('minimumTlsVersion')]"
},
{
"field": "Microsoft.Storage/storageAccounts/supportsHttpsTrafficOnly",
"notEquals": true
},
{
"field": "Microsoft.Storage/storageAccounts/allowBlobPublicAccess",
"notEquals": false
}
]
}
]
},
"then": {
"effect": "[parameters('effect')]"
}
}
}
}
Policy Initiative (Policy Set) for CIS Benchmark
{
"properties": {
"displayName": "CIS Microsoft Azure Foundations Benchmark v2.0",
"description": "Enterprise compliance initiative covering CIS Azure controls",
"metadata": {
"category": "Compliance",
"version": "2.0.0"
},
"policyDefinitions": [
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/storage-cmk-policy",
"parameters": { "effect": { "value": "Deny" } },
"groupNames": ["CIS-2.1"]
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/sql-tde-policy",
"parameters": { "effect": { "value": "DeployIfNotExists" } },
"groupNames": ["CIS-4.1"]
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/nsg-flow-logs-policy",
"parameters": { "effect": { "value": "DeployIfNotExists" } },
"groupNames": ["CIS-6.4"]
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/keyvault-soft-delete-policy",
"parameters": { "effect": { "value": "Deny" } },
"groupNames": ["CIS-8.4"]
}
],
"policyDefinitionGroups": [
{ "name": "CIS-2.1", "displayName": "2.1 - Storage Account Security" },
{ "name": "CIS-4.1", "displayName": "4.1 - SQL Database Encryption" },
{ "name": "CIS-6.4", "displayName": "6.4 - Network Security Group Flow Logs" },
{ "name": "CIS-8.4", "displayName": "8.4 - Key Vault Configuration" }
]
}
}
Policy Deployment Pipeline
# azure-pipelines/policy-deployment.yaml
trigger:
branches:
include: [main]
paths:
include: [policies/**]
pool:
vmImage: 'ubuntu-latest'
stages:
- stage: Validate
jobs:
- job: PolicyValidation
steps:
- task: AzurePowerShell@5
displayName: 'Validate Policy Definitions'
inputs:
azureSubscription: 'governance-service-connection'
ScriptType: InlineScript
Inline: |
$policyFiles = Get-ChildItem -Path "$(Build.SourcesDirectory)/policies/definitions" -Filter "*.json" -Recurse
foreach ($file in $policyFiles) {
$policy = Get-Content $file.FullName | ConvertFrom-Json
Write-Host "Validating: $($policy.properties.displayName)"
# Validate policy rule syntax
if (-not $policy.properties.policyRule) {
throw "Missing policyRule in $($file.Name)"
}
# Check for required metadata
if (-not $policy.properties.metadata.version) {
throw "Missing version metadata in $($file.Name)"
}
}
azurePowerShellVersion: LatestVersion
- stage: Deploy
dependsOn: Validate
jobs:
- deployment: DeployPolicies
environment: 'governance-prod'
strategy:
runOnce:
deploy:
steps:
- task: AzureCLI@2
displayName: 'Deploy Policy Definitions'
inputs:
azureSubscription: 'governance-service-connection'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
for file in policies/definitions/*.json; do
name=$(basename "$file" .json)
az policy definition create \
--name "$name" \
--management-group "mg-contoso-root" \
--rules "$file" \
--mode "Indexed"
done
- task: AzureCLI@2
displayName: 'Assign Policy Initiatives'
inputs:
azureSubscription: 'governance-service-connection'
scriptType: bash
scriptLocation: inlineScript
inlineScript: |
az policy assignment create \
--name "cis-benchmark-v2" \
--policy-set-definition "cis-azure-v2" \
--scope "/providers/Microsoft.Management/managementGroups/mg-contoso-root" \
--mi-system-assigned \
--location "eastus2" \
--enforcement-mode "Default"
Phase 3: Azure Arc for Multi-Cloud Management
Onboarding AWS EC2 Instances
# Generate Arc onboarding script for AWS fleet
$servicePrincipal = New-AzADServicePrincipal `
-DisplayName "arc-onboarding-aws" `
-Role "Azure Connected Machine Onboarding" `
-Scope "/subscriptions/$subscriptionId/resourceGroups/rg-arc-servers"
# Create onboarding script for AWS instances
$onboardingScript = @"
#!/bin/bash
# Azure Arc onboarding for AWS EC2 instances
# Download and install Arc agent
wget https://aka.ms/azcmagent -O ~/install_linux_azcmagent.sh
sudo bash ~/install_linux_azcmagent.sh
# Connect to Azure Arc
sudo azcmagent connect \
--resource-group "rg-arc-servers" \
--tenant-id "$tenantId" \
--location "eastus2" \
--subscription-id "$subscriptionId" \
--service-principal-id "$($servicePrincipal.AppId)" \
--service-principal-secret "$servicePrincipalSecret" \
--cloud "AzureCloud" \
--tags "Source=AWS,Region=us-east-1,Environment=Production" \
--correlation-id "$(New-Guid)"
# Verify connection
sudo azcmagent show
"@
# Deploy via AWS Systems Manager
$ssmCommand = @{
DocumentName = "AWS-RunShellScript"
Targets = @(@{Key="tag:ArcManaged"; Values=@("true")})
Parameters = @{
commands = @($onboardingScript)
}
MaxConcurrency = "50"
MaxErrors = "10"
}
Arc-Enabled Policy Enforcement
{
"properties": {
"displayName": "Deploy Log Analytics agent to Arc-enabled servers",
"policyType": "Custom",
"mode": "Indexed",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.HybridCompute/machines"
},
{
"field": "Microsoft.HybridCompute/machines/osType",
"equals": "linux"
}
]
},
"then": {
"effect": "DeployIfNotExists",
"details": {
"type": "Microsoft.HybridCompute/machines/extensions",
"roleDefinitionIds": [
"/providers/Microsoft.Authorization/roleDefinitions/92aaf0da-9dab-42b6-94a3-d43ce8d16293"
],
"existenceCondition": {
"allOf": [
{
"field": "Microsoft.HybridCompute/machines/extensions/type",
"equals": "OmsAgentForLinux"
},
{
"field": "Microsoft.HybridCompute/machines/extensions/provisioningState",
"equals": "Succeeded"
}
]
},
"deployment": {
"properties": {
"mode": "incremental",
"template": {
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"resources": [
{
"type": "Microsoft.HybridCompute/machines/extensions",
"apiVersion": "2022-12-27",
"name": "[concat(field('name'), '/OmsAgentForLinux')]",
"location": "[field('location')]",
"properties": {
"publisher": "Microsoft.EnterpriseCloud.Monitoring",
"type": "OmsAgentForLinux",
"autoUpgradeMinorVersion": true,
"settings": {
"workspaceId": "[parameters('logAnalyticsWorkspaceId')]"
},
"protectedSettings": {
"workspaceKey": "[parameters('logAnalyticsWorkspaceKey')]"
}
}
}
]
}
}
}
}
}
}
}
}

Phase 4: Cost Management and FinOps
Cost Allocation and Showback
# Configure cost allocation rules
$costRule = @{
name = "SharedInfraAllocation"
description = "Allocate shared platform costs to business units"
sourceScopes = @("/subscriptions/platform-sub-id")
targetScopes = @(
"/subscriptions/bu-engineering-sub-id",
"/subscriptions/bu-marketing-sub-id",
"/subscriptions/bu-finance-sub-id"
)
details = @{
type = "Proportional"
splitRule = @{
property = "ResourceGroup"
splitValues = @(
@{ name = "rg-shared-networking"; percentage = 40 },
@{ name = "rg-shared-security"; percentage = 35 },
@{ name = "rg-shared-monitoring"; percentage = 25 }
)
}
}
}
# Create budget alerts
$budget = @{
name = "Q1-2026-Engineering"
amount = 250000
timeGrain = "Quarterly"
timePeriod = @{
startDate = "2026-01-01"
endDate = "2026-03-31"
}
filter = @{
tags = @{
name = "BusinessUnit"
values = @("Engineering")
}
}
notifications = @{
"Forecast80" = @{
enabled = $true
operator = "GreaterThan"
threshold = 80
thresholdType = "Forecasted"
contactEmails = @("engineering-leads@contoso.com")
contactRoles = @("Owner")
}
"Actual100" = @{
enabled = $true
operator = "GreaterThan"
threshold = 100
thresholdType = "Actual"
contactEmails = @("cfo@contoso.com", "engineering-vp@contoso.com")
}
}
}
Automated Cost Optimization
from azure.mgmt.advisor import AdvisorManagementClient
from azure.mgmt.compute import ComputeManagementClient
from azure.identity import DefaultAzureCredential
credential = DefaultAzureCredential()
# Get cost optimization recommendations
advisor_client = AdvisorManagementClient(credential, subscription_id)
recommendations = advisor_client.recommendations.list(
filter="Category eq 'Cost'"
)
savings_report = []
for rec in recommendations:
savings_report.append({
"resource": rec.resource_metadata.resource_id,
"recommendation": rec.short_description.problem,
"solution": rec.short_description.solution,
"annual_savings": rec.extended_properties.get("annualSavingsAmount", "Unknown"),
"impact": rec.impact
})
# Auto-resize underutilized VMs
compute_client = ComputeManagementClient(credential, subscription_id)
for rec in savings_report:
if "right-size" in rec["solution"].lower() and rec["impact"] == "High":
resource_id = rec["resource"]
vm_name = resource_id.split("/")[-1]
rg_name = resource_id.split("/resourceGroups/")[1].split("/")[0]
vm = compute_client.virtual_machines.get(rg_name, vm_name)
# Log recommendation for approval workflow
print(f"VM: {vm_name}")
print(f" Current size: {vm.hardware_profile.vm_size}")
print(f" Recommended: {rec['solution']}")
print(f" Annual savings: ${rec['annual_savings']}")

Governance Maturity Model
| Capability | Level 1 (Ad-hoc) | Level 2 (Managed) | Level 3 (Optimized) |
|---|---|---|---|
| Policy | Manual reviews | Policy-as-Code deployed | Auto-remediation enabled |
| Cost | Reactive billing | Budget alerts + showback | Predictive optimization |
| Identity | Per-resource RBAC | Landing Zone RBAC templates | PIM + JIT everywhere |
| Compliance | Annual audits | Continuous compliance monitoring | Real-time remediation |
| Multi-Cloud | Separate dashboards | Arc-enabled monitoring | Unified policy enforcement |
| Landing Zones | Manual setup | Subscription vending | Self-service portal |
Best Practices
- Start with management group hierarchy: This is the foundation — getting it wrong is expensive to fix
- Policy-as-Code in CI/CD: Never deploy policies manually — version control and test everything
- Tagging is governance: Enforce mandatory tags (CostCenter, Owner, Environment) from day one
- Subscription vending over shared subscriptions: Blast radius isolation is worth the management overhead
- Arc everything: On-premises servers, AWS VMs, edge devices — bring them all under Azure governance
- FinOps is a practice, not a tool: Combine Cost Management alerts with organizational accountability
Architecture Decision and Tradeoffs
When designing integrated solutions solutions with Azure + Power Platform, consider these key architectural trade-offs:
| Approach | Best For | Tradeoff |
|---|---|---|
| Managed / platform service | Rapid delivery, reduced ops burden | Less customisation, potential vendor lock-in |
| Custom / self-hosted | Full control, advanced tuning | Higher operational overhead and cost |
Recommendation: Start with the managed approach for most workloads and move to custom only when specific requirements demand it.
Validation and Versioning
- Last validated: April 2026
- Validate examples against your tenant, region, and SKU constraints before production rollout.
- Keep module, CLI, and SDK versions pinned in automation pipelines and review quarterly.
Security and Governance Considerations
- Apply least-privilege access using RBAC roles and just-in-time elevation for admin tasks.
- Store secrets in managed secret stores and avoid embedding credentials in scripts or source files.
- Enable audit logging, data protection policies, and periodic access reviews for regulated workloads.
Cost and Performance Notes
- Define budgets and alerts, then monitor usage and cost trends continuously after go-live.
- Baseline performance with synthetic and real-user checks before and after major changes.
- Scale resources with measured thresholds and revisit sizing after usage pattern changes.
Official Microsoft References
- https://learn.microsoft.com/azure/architecture/
- https://learn.microsoft.com/azure/well-architected/
- https://learn.microsoft.com/power-platform/guidance/
Public Examples from Official Sources
- These examples are sourced from official public Microsoft documentation and sample repositories.
- Documentation examples: https://learn.microsoft.com/azure/well-architected/
- Sample repositories: https://github.com/Azure/ArchitectureCenter
- Prefer adapting these examples to your tenant, subscriptions, and governance requirements before production use.
Key Takeaways
- Azure Landing Zones provide the standardized foundation every enterprise needs
- Azure Policy enforces guardrails consistently across 1,000+ subscriptions
- Azure Arc extends Azure governance to AWS, on-premises, and edge resources
- Cost Management with FinOps practices prevents cloud bill shock
- Policy-as-Code enables version control, testing, and audit trails for all governance rules