Zum Hauptinhalt springen
Alle Beiträge
DevSecOps9 Min. Lesezeit

Azure Policy as Code: Governance im großen Maßstab durchsetzen, ohne Deployments zu blockieren

Ein umfassender Leitfaden zur Implementierung von Azure Policy as Code mit Lifecycle Management, Policy-Definitionen in Bicep und Terraform, Initiative Bundles, Exemption Management, Compliance Dashboards und Remediation Tasks.

Veröffentlicht

Azure Policy ist das Leitplanken-System für Ihre Cloud-Umgebung. Es kann Tagging-Standards durchsetzen, unsichere Konfigurationen blockieren und die Einhaltung regulatorischer Anforderungen sicherstellen — alles automatisch. Aber die meisten Unternehmen implementieren es falsch: Sie erstellen Policies über das Portal, vergessen sie zu testen und wundern sich dann, warum ein kritisches Deployment am Freitagnachmittag fehlschlägt, weil jemand den Effect einer Policy von Audit auf Deny geändert hat, ohne jemanden zu informieren.

Dieser Beitrag behandelt, wie Sie Azure Policy als Code verwalten: Authoring, Testing, Deployment, Monitoring und den Umgang mit den unvermeidlichen Exemptions.

Der Policy-as-Code-Lebenszyklus

Policy Management folgt demselben Lebenszyklus wie Anwendungscode:

Loading diagram...

Jede Phase hat spezifische Praktiken:

Authoring — Schreiben Sie Policy-Definitionen in Ihrem IaC-Tool (Bicep oder Terraform). Speichern Sie sie in einem dedizierten Repository oder einem policies/-Verzeichnis in Ihrem Landing Zone Repo.

Review — Pull-Request-Review mit Genehmigung sowohl vom Platform Team als auch vom Security Team erforderlich. Policy-Änderungen betreffen jedes Team in der Organisation.

Test — Deployment in eine Test Management Group und Überprüfung, ob die Policy wie erwartet funktioniert. Bestätigung, dass sie Verstöße erkennt, ohne False Positives.

Deploy (Audit) — Deployment in Production Management Groups im Audit-Modus. Compliance 2-4 Wochen lang überwachen.

Monitoring — Compliance-Daten überprüfen. Werden legitime Ressourcen markiert? Policy-Regel anpassen oder Exemptions hinzufügen.

Enforce (Deny) — Auf Deny umstellen, sobald die Compliance über 95% liegt und alle legitimen Exemptions dokumentiert sind.

Wartung — Policies vierteljährlich überprüfen. Veraltete Policies entfernen. Regeln aktualisieren, wenn Azure-Dienste sich weiterentwickeln.

Loading diagram...

Policy-Definitionen in Bicep

Hier sind praxisnahe Policy-Definitionen für gängige Enterprise-Governance-Regeln.

Tags auf Resource Groups vorschreiben

Bicep
// policies/require-tags-rg.bicep
targetScope = 'managementGroup'

@description('Liste der erforderlichen Tag-Namen')
param requiredTags array = [
  'Environment'
  'CostCenter'
  'Owner'
  'Application'
]

resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2023-04-01' = {
  name: 'require-tags-on-resource-groups'
  properties: {
    displayName: 'Bestimmte Tags auf Resource Groups vorschreiben'
    description: 'Stellt sicher, dass Resource Groups erforderliche Tags für Kostenmanagement und Ownership-Tracking haben'
    policyType: 'Custom'
    mode: 'All'
    metadata: {
      category: 'Tags'
      version: '1.2.0'
    }
    parameters: {
      effect: {
        type: 'String'
        metadata: {
          displayName: 'Effect'
          description: 'Deny oder Audit der Policy'
        }
        allowedValues: ['Audit', 'Deny']
        defaultValue: 'Audit'
      }
    }
    policyRule: {
      if: {
        allOf: [
          {
            field: 'type'
            equals: 'Microsoft.Resources/subscriptions/resourceGroups'
          }
          {
            anyOf: [for tag in requiredTags: {
              field: 'tags[\'${tag}\']'
              exists: 'false'
            }]
          }
        ]
      }
      then: {
        effect: '[parameters(\'effect\')]'
      }
    }
  }
}

Public IP-Adressen verbieten (mit Ausnahmen)

Bicep
// policies/deny-public-ip.bicep
targetScope = 'managementGroup'

resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2023-04-01' = {
  name: 'deny-public-ip-addresses'
  properties: {
    displayName: 'Erstellung von Public IP-Adressen verbieten'
    description: 'Verhindert die Erstellung von Public IP-Adressen außer in genehmigten Resource Groups'
    policyType: 'Custom'
    mode: 'All'
    metadata: {
      category: 'Network'
      version: '2.0.0'
    }
    parameters: {
      effect: {
        type: 'String'
        allowedValues: ['Audit', 'Deny']
        defaultValue: 'Deny'
      }
      excludedResourceGroups: {
        type: 'Array'
        metadata: {
          displayName: 'Ausgeschlossene Resource Groups'
          description: 'Resource Groups, in denen Public IPs erlaubt sind (z.B. DMZ, Bastion)'
        }
        defaultValue: []
      }
    }
    policyRule: {
      if: {
        allOf: [
          {
            field: 'type'
            equals: 'Microsoft.Network/publicIPAddresses'
          }
          {
            field: 'Microsoft.Network/publicIPAddresses/publicIPAllocationMethod'
            exists: 'true'
          }
          {
            value: '[resourceGroup().name]'
            notIn: '[parameters(\'excludedResourceGroups\')]'
          }
        ]
      }
      then: {
        effect: '[parameters(\'effect\')]'
      }
    }
  }
}

TLS 1.2 Minimum auf Storage Accounts erzwingen

Bicep
// policies/enforce-tls-storage.bicep
targetScope = 'managementGroup'

resource policyDefinition 'Microsoft.Authorization/policyDefinitions@2023-04-01' = {
  name: 'enforce-tls12-storage'
  properties: {
    displayName: 'TLS 1.2 Minimum auf Storage Accounts erzwingen'
    description: 'Storage Accounts müssen TLS 1.2 oder höher verwenden'
    policyType: 'Custom'
    mode: 'Indexed'
    metadata: {
      category: 'Storage'
      version: '1.0.0'
    }
    parameters: {
      effect: {
        type: 'String'
        allowedValues: ['Audit', 'Deny', 'Modify']
        defaultValue: 'Modify'
      }
    }
    policyRule: {
      if: {
        allOf: [
          {
            field: 'type'
            equals: 'Microsoft.Storage/storageAccounts'
          }
          {
            field: 'Microsoft.Storage/storageAccounts/minimumTlsVersion'
            notEquals: 'TLS1_2'
          }
        ]
      }
      then: {
        effect: '[parameters(\'effect\')]'
        details: {
          roleDefinitionIds: [
            '/providers/Microsoft.Authorization/roleDefinitions/17d1049b-9a84-46fb-8f53-869881c3d3ab'
          ]
          conflictEffect: 'audit'
          operations: [
            {
              operation: 'addOrReplace'
              field: 'Microsoft.Storage/storageAccounts/minimumTlsVersion'
              value: 'TLS1_2'
            }
          ]
        }
      }
    }
  }
}

Policy-Definitionen in Terraform

Wenn Ihre Landing Zone Terraform verwendet, hier der äquivalente Ansatz:

HCL
# policies/deny_public_ip/main.tf
resource "azurerm_policy_definition" "deny_public_ip" {
  name                = "deny-public-ip-addresses"
  display_name        = "Erstellung von Public IP-Adressen verbieten"
  description         = "Verhindert die Erstellung von Public IP-Adressen außer in genehmigten Resource Groups"
  policy_type         = "Custom"
  mode                = "All"
  management_group_id = var.management_group_id

  metadata = jsonencode({
    category = "Network"
    version  = "2.0.0"
  })

  parameters = jsonencode({
    effect = {
      type = "String"
      metadata = {
        displayName = "Effect"
      }
      allowedValues = ["Audit", "Deny"]
      defaultValue  = "Deny"
    }
    excludedResourceGroups = {
      type = "Array"
      metadata = {
        displayName = "Ausgeschlossene Resource Groups"
      }
      defaultValue = []
    }
  })

  policy_rule = jsonencode({
    if = {
      allOf = [
        {
          field  = "type"
          equals = "Microsoft.Network/publicIPAddresses"
        },
        {
          field  = "Microsoft.Network/publicIPAddresses/publicIPAllocationMethod"
          exists = "true"
        },
        {
          value = "[resourceGroup().name]"
          notIn = "[parameters('excludedResourceGroups')]"
        }
      ]
    }
    then = {
      effect = "[parameters('effect')]"
    }
  })
}

Terraform Policy Assignment

HCL
# assignments/production.tf
resource "azurerm_management_group_policy_assignment" "deny_public_ip" {
  name                 = "deny-public-ip-prod"
  management_group_id  = data.azurerm_management_group.production.id
  policy_definition_id = azurerm_policy_definition.deny_public_ip.id

  parameters = jsonencode({
    effect                 = { value = "Deny" }
    excludedResourceGroups = { value = ["rg-dmz-prod", "rg-bastion-prod"] }
  })

  non_compliance_message {
    content = "Public IP-Adressen sind nicht erlaubt. Verwenden Sie stattdessen Private Endpoints oder Azure Front Door. Kontaktieren Sie platform-team@company.com für Ausnahmen."
  }

  identity {
    type = "SystemAssigned"
  }

  location = "westeurope"
}

Initiative Bundles

Gruppieren Sie verwandte Policies in Initiatives (Policy Sets) für einfachere Zuweisung:

HCL
# initiatives/security-baseline.tf
resource "azurerm_policy_set_definition" "security_baseline" {
  name                = "security-baseline-initiative"
  display_name        = "Enterprise Security Baseline"
  description         = "Kern-Sicherheitspolicies, die auf alle Subscriptions angewendet werden"
  policy_type         = "Custom"
  management_group_id = var.root_management_group_id

  metadata = jsonencode({
    category = "Security"
    version  = "3.1.0"
  })

  parameters = jsonencode({
    storageEffect = {
      type         = "String"
      defaultValue = "Deny"
    }
    networkEffect = {
      type         = "String"
      defaultValue = "Deny"
    }
  })

  policy_definition_reference {
    policy_definition_id = azurerm_policy_definition.enforce_tls_storage.id
    parameter_values = jsonencode({
      effect = { value = "[parameters('storageEffect')]" }
    })
    reference_id = "enforceTlsStorage"
  }

  policy_definition_reference {
    policy_definition_id = azurerm_policy_definition.deny_public_ip.id
    parameter_values = jsonencode({
      effect = { value = "[parameters('networkEffect')]" }
    })
    reference_id = "denyPublicIp"
  }

  policy_definition_reference {
    policy_definition_id = "/providers/Microsoft.Authorization/policyDefinitions/404c3081-a854-4457-ae30-26a93ef643f9"
    reference_id = "secureTransferStorage"
  }
}

Weisen Sie die Initiative einmal einer Management Group zu, und alle Mitglieds-Subscriptions erben die Policies.

Exemption Management

Exemptions sind unvermeidlich. Der Schlüssel ist, sie als Code mit Ablaufdatum zu verwalten:

HCL
# exemptions/payment-team-public-ip.tf
resource "azurerm_resource_policy_exemption" "payment_gateway_public_ip" {
  name                 = "payment-gateway-public-ip-exemption"
  resource_id          = data.azurerm_resource_group.payment_gateway.id
  policy_assignment_id = azurerm_management_group_policy_assignment.deny_public_ip.id

  exemption_category = "Waiver"
  description        = "Payment Gateway benötigt Public IP für PCI DSS-konformen externen Endpoint. Genehmigt vom Security Team in JIRA-SEC-1234."

  expires_on = "2026-06-30T00:00:00Z"

  metadata = jsonencode({
    approvedBy    = "security-team"
    ticketNumber  = "JIRA-SEC-1234"
    reviewDate    = "2026-06-15"
    justification = "PCI DSS Anforderung für Payment Processor Callback Endpoint"
  })
}

Governance-Regeln für Exemptions:

  1. Jede Exemption muss eine JIRA/ADO-Ticket-Referenz haben
  2. Maximale Exemption-Dauer: 6 Monate (verlängerbar mit erneutem Review)
  3. Exemptions erfordern die Genehmigung des Security Teams im Pull Request
  4. Ein monatlicher Report listet alle aktiven Exemptions auf, die sich dem Ablauf nähern
  5. Abgelaufene Exemptions werden automatisch von der Pipeline entfernt
YAML
# azure-pipelines.yml — Exemption Cleanup
schedules:
  - cron: '0 8 * * 1'
    displayName: 'Wöchentlicher Exemption Review'
    branches:
      include: [main]

steps:
  - script: |
      # Exemptions finden, die in den nächsten 14 Tagen ablaufen
      az policy exemption list \
        --query "[?properties.expiresOn < '$(date -d '+14 days' -u +%Y-%m-%dT%H:%M:%SZ)']" \
        --output table
    displayName: 'Ablaufende Exemptions melden'

Compliance Dashboards

Azure Policy bietet integrierte Compliance-Ansichten, aber für Enterprise-Reporting brauchen Sie mehr:

Azure Resource Graph Queries

Kusto
// Gesamtcompliance nach Management Group
PolicyResources
| where type == 'microsoft.policyinsights/policystates'
| where properties.complianceState != 'Compliant'
| summarize NonCompliantCount = count() by
    ManagementGroup = tostring(properties.managementGroupIds),
    PolicyName = tostring(properties.policyDefinitionName),
    Category = tostring(properties.policyDefinitionCategory)
| order by NonCompliantCount desc
Kusto
// Nicht-konforme Ressourcen mit Details
PolicyResources
| where type == 'microsoft.policyinsights/policystates'
| where properties.complianceState == 'NonCompliant'
| project
    ResourceId = tostring(properties.resourceId),
    ResourceType = tostring(properties.resourceType),
    PolicyName = tostring(properties.policyDefinitionName),
    Subscription = tostring(properties.subscriptionId),
    Timestamp = todatetime(properties.timestamp)
| order by Timestamp desc
| take 100

Remediation Tasks

Loading diagram...

Policies mit Modify- oder DeployIfNotExists-Effect können nicht-konforme Ressourcen automatisch remediaten:

HCL
# remediation/tls-remediation.tf
resource "azurerm_resource_group_policy_remediation" "tls_remediation" {
  name                 = "remediate-tls-storage"
  resource_group_id    = data.azurerm_resource_group.example.id
  policy_assignment_id = azurerm_management_group_policy_assignment.security_baseline.id

  policy_definition_reference_id = "enforceTlsStorage"

  resource_discovery_mode = "ReEvaluateCompliance"
}

Vorsicht bei Remediation: Testen Sie Remediation Tasks immer zuerst in einer Non-Production Subscription. Eine Modify-Policy, die das falsche Feld aktualisiert, kann laufende Services beschädigen. Verwenden Sie resource_discovery_mode = "ReEvaluateCompliance", um frische Compliance-Daten vor der Remediation zu erhalten.

CI/CD-Pipeline für Policy Deployment

YAML
# azure-pipelines.yml — Policy Deployment
trigger:
  branches:
    include: [main]
  paths:
    include: [policies/**, initiatives/**, assignments/**]

stages:
  - stage: Validate
    jobs:
      - job: PolicyTest
        pool:
          vmImage: 'ubuntu-latest'
        steps:
          - script: |
              # Alle Policy-JSON-Dateien syntaktisch validieren
              for f in policies/**/policy-rule.json; do
                jq empty "$f" || exit 1
              done
            displayName: 'Policy JSON validieren'

          - script: terraform plan -target=module.policies
            displayName: 'Terraform Plan — nur Policies'

  - stage: DeployTest
    dependsOn: Validate
    jobs:
      - deployment: DeployToTestMG
        environment: policy-test
        strategy:
          runOnce:
            deploy:
              steps:
                - script: |
                    terraform apply -target=module.policies -auto-approve
                  displayName: 'Policies in Test Management Group deployen'

  - stage: DeployProd
    dependsOn: DeployTest
    jobs:
      - deployment: DeployToProdMG
        environment: policy-production
        strategy:
          runOnce:
            deploy:
              steps:
                - script: |
                    terraform apply -target=module.policies -auto-approve
                  displayName: 'Policies in Production Management Groups deployen'

Häufige Fallstricke

1. Mit Deny-Effect starten. Beginnen Sie immer mit Audit. Messen Sie die Auswirkungen. Dann erzwingen Sie. Eine Deny-Policy, die ohne Tests deployt wird, blockiert Deployments und erzeugt Notfall-Tickets.

2. Policies nicht versionieren. Fügen Sie ein version-Feld in die Policy-Metadata ein. Wenn Sie eine Policy aktualisieren, erhöhen Sie die Version. Dies ermöglicht es nachzuverfolgen, auf welche Version einer Policy sich ein Compliance-Finding bezieht.

3. Evaluierungsverzögerung ignorieren. Die Azure Policy-Evaluierung ist nicht sofort. Neue Ressourcen werden innerhalb von 15 Minuten evaluiert. Bestehende Ressourcen werden alle 24 Stunden neu evaluiert. Erwarten Sie keine Echtzeit-Compliance-Daten.

4. Zu viele Custom Policies. Prüfen Sie vor dem Schreiben einer Custom Policy die über 800 integrierten Policies. Viele gängige Governance-Regeln existieren bereits und werden von Microsoft gewartet.

5. Keine Non-Compliance-Messages. Eine generische Fehlermeldung "Policy hat die Anfrage abgelehnt" verschwendet Entwicklerzeit. Fügen Sie immer eine non_compliance_message ein, die erklärt, was falsch ist und wie man es behebt.

Fazit

Azure Policy as Code transformiert Governance von einer ad-hoc Portal-Aktivität in einen disziplinierten, überprüfbaren, testbaren Prozess. Die Investition in die Repository-Struktur, CI/CD-Pipeline und den Exemption-Prozess amortisiert sich beim ersten Mal, wenn eine Policy-Änderung ein ordentliches Review durchläuft, anstatt freitags um 16 Uhr in die Produktion geklickt zu werden.

Beginnen Sie mit drei bis fünf Policies, die Ihre höchsten Risiko-Fehlkonfigurationen adressieren. Deployen Sie sie im Audit-Modus. Bauen Sie das Compliance Dashboard. Erweitern Sie dann schrittweise die Abdeckung und wechseln Sie zu Deny, während sich die Teams anpassen.

Wenn Sie Hilfe bei der Gestaltung Ihres Azure Governance Frameworks oder der Implementierung von Policy as Code für Ihre Landing Zone benötigen, kontaktieren Sie uns unter mbrahim@conceptualise.de. Wir helfen Unternehmen dabei, Governance aufzubauen, die skaliert, ohne Engpässe zu erzeugen.

Themen

Azure Policy as CodeCloud Governance im großen MaßstabAzure Policy Terraform BicepPolicy Compliance AutomatisierungEnterprise Governance Azure

Häufig gestellte Fragen

Azure Policy as Code bedeutet, Ihre Azure Policy-Definitionen, -Zuweisungen und -Ausnahmen in versionskontrollierten Repositories zu verwalten, anstatt durch das Azure Portal zu klicken. Es ist wichtig, weil im Portal verwaltete Policies im großen Maßstab unregierbar werden — Sie können Änderungen nicht überprüfen, nicht nachverfolgen, wer was geändert hat, und nicht konsistent dieselben Policies über mehrere Management Groups hinweg deployen. Code-basierte Policies erhalten dieselbe Review-, Test- und Deployment-Rigorosität wie Anwendungscode.

Expert engagement

Brauchen Sie Expertenberatung?

Unser Team ist spezialisiert auf Cloud-Architektur, Security, KI-Plattformen und DevSecOps. Lassen Sie uns besprechen, wie wir Ihrem Unternehmen helfen können.

Kontakt aufnehmenNo commitment · No sales pressure

Verwandte Artikel

Alle Beiträge