Azure Policy ile Kaynak Uyumluluk Yönetimi
Bulut ortamlarında kaynak yönetimi, ekipler büyüdükçe ve altyapı karmaşıklaştıkça giderek daha kritik bir hal alıyor. “Kim hangi kaynağı neden oluşturdu?”, “Neden bu VM’in etiketi yok?”, “Bu storage account neden public erişime açık?” gibi sorularla boğuşan her sysadmin, er ya da geç Azure Policy ile tanışmak zorunda kalıyor. Bu yazıda Azure Policy’yi sıfırdan kurgulamaktan, gerçek dünyada karşılaşılan senaryolara çözüm üretmeye kadar her şeyi ele alacağız.
Azure Policy Nedir ve Neden Önemlidir
Azure Policy, Azure kaynaklarınız üzerinde organizasyonel standartları uygulamanızı ve uyumluluk değerlendirmesi yapmanızı sağlayan bir yönetim servisidir. Basitçe söylemek gerekirse: “Şu kurala uymayan hiçbir kaynak oluşturulamasın” veya “Mevcut kaynaklar bu kurala uyuyor mu?” sorularının cevabını veren mekanizmadır.
Peki neden bu kadar önemli? Düşünün, 50 kişilik bir DevOps ekibiniz var. Her ekip üyesi kendi ihtiyacına göre kaynak oluşturuyor. Bir süre sonra dönersiniz ve şunu görürsünüz:
- Yarısının etiketi yok, dolayısıyla maliyet takibi imkansız
- Bazı storage account’lar public erişime açık bırakılmış
- Bazı VM’ler production subscription’da test amaçlı oluşturulmuş ama silinmemiş
- Belirli bölgelerde oluşturulmaması gereken kaynaklar orada çalışıyor
Azure Policy olmadan bu durumu düzeltmek için ya herkese tek tek mail atarsınız (ve çoğu zaman sonuç almazsınız) ya da sürekli manuel audit yaparsınız. Azure Policy ile bu kuralları merkezi olarak tanımlayıp otomatik olarak uygulayabilirsiniz.
Temel Kavramlar
Azure Policy dünyasına girmeden önce birkaç temel kavramı netleştirmek gerekiyor.
Policy Definition
Bir policy definition, tek bir kuralı tanımlayan JSON belgesidir. “Tüm kaynakların ‘Environment’ etiketi olmalıdır” veya “SQL Server’lar TLS 1.2 kullanmalıdır” gibi tek bir standardı ifade eder.
Initiative (Policy Set Definition)
Birden fazla policy definition’ı bir araya getiren koleksiyondur. Microsoft’un hazır sunduğu “Azure Security Benchmark” veya “CIS Microsoft Azure Foundations Benchmark” gibi setler birer initiative’dir. Kendi özel initiative’lerinizi de oluşturabilirsiniz.
Assignment
Policy veya initiative’lerin belirli bir scope’a (management group, subscription, resource group) uygulanmasıdır. Bir policy’yi tanımlamak yetmez, onu bir scope’a atamak gerekir.
Effect
Policy’nin ihlal durumunda ne yapacağını belirler. En sık kullanılanlar şunlardır:
- Deny: Kurala uymayan kaynağın oluşturulmasını tamamen engeller
- Audit: Kurala uymayan kaynağı işaretler ama engellemez
- AuditIfNotExists: Belirtilen bir kaynak yoksa audit eder
- DeployIfNotExists: Belirtilen bir kaynak yoksa otomatik olarak deploy eder
- Modify: Kaynağa otomatik olarak özellik ekler veya değiştirir
- Append: Kaynağa ek alan ekler
Azure CLI ile Policy Yönetimi
Azure Portal üzerinden policy oluşturabilirsiniz ama gerçek otomasyon için Azure CLI veya PowerShell şart. Ben CLI tarafını tercih ediyorum çünkü script’lere daha iyi entegre oluyor.
Önce ortamınızı hazırlayalım:
# Azure CLI ile login
az login
# Aktif subscription'ı kontrol et
az account show --query "{Name:name, ID:id}" -o table
# Belirli bir subscription'ı aktif yap
az account set --subscription "production-subscription-id"
# Mevcut policy definition'larını listele
az policy definition list --query "[?policyType=='BuiltIn'].{Name:displayName, Type:policyType}" -o table | head -20
İlk Policy Assignment: Etiket Zorunluluğu
En yaygın kullanım senaryolarından biri zorunlu etiket politikasıdır. Maliyet merkezleri, ortam tipi ve sahiplik bilgisi olmayan kaynaklar FinOps ekiplerinin kabusu haline gelir.
# Built-in policy definition ID'sini bul
POLICY_ID=$(az policy definition list
--query "[?displayName=='Require a tag on resources'].id"
-o tsv)
echo "Policy ID: $POLICY_ID"
# Resource group scope'unda assignment oluştur
az policy assignment create
--name "require-environment-tag"
--display-name "Ortam Etiketi Zorunluluğu"
--policy "$POLICY_ID"
--scope "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/production-rg"
--params '{"tagName": {"value": "Environment"}}'
--enforcement-mode Default
echo "Policy assignment başarıyla oluşturuldu"
Bu assignment’tan sonra production-rg içinde Environment etiketi olmayan hiçbir kaynak oluşturulamaz.
Özel Policy Definition Oluşturma
Built-in policy’ler çoğu senaryoyu karşılasa da bazen kendi kurallarınızı yazmanız gerekir. Örneğin şirkete özel bir standart uygulamak istiyorsunuz: “Tüm VM’lerin ismi ‘prod-‘, ‘dev-‘ veya ‘test-‘ ile başlamalıdır.”
# Policy definition JSON dosyasını oluştur
cat > vm-naming-policy.json << 'EOF'
{
"mode": "All",
"policyRule": {
"if": {
"allOf": [
{
"field": "type",
"equals": "Microsoft.Compute/virtualMachines"
},
{
"not": {
"anyOf": [
{
"field": "name",
"like": "prod-*"
},
{
"field": "name",
"like": "dev-*"
},
{
"field": "name",
"like": "test-*"
}
]
}
}
]
},
"then": {
"effect": "Deny"
}
},
"parameters": {}
}
EOF
# Policy definition'ı oluştur
az policy definition create
--name "vm-naming-convention"
--display-name "VM İsimlendirme Standardı"
--description "VM isimleri prod-, dev- veya test- ile başlamalıdır"
--rules vm-naming-policy.json
--mode All
--subscription $(az account show --query id -o tsv)
echo "Custom policy definition oluşturuldu"
DeployIfNotExists ile Otomatik Remediation
Audit mode işaretler ama düzeltmez. Gerçekten güçlü olan senaryo, uyumsuz kaynakları otomatik olarak düzelten policy’lerdir. Örneğin, her VM oluşturulduğunda otomatik olarak bir diagnostic setting ekleyelim:
# Managed Identity ile policy assignment oluştur (remediation için şart)
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
az policy assignment create
--name "vm-diagnostics-auto-deploy"
--display-name "VM Diagnostics Otomatik Kurulum"
--policy "/providers/Microsoft.Authorization/policyDefinitions/a3a6ea0c-e33d-45d0-9892-4b7e0c62e8ca"
--scope "/subscriptions/$SUBSCRIPTION_ID"
--location "westeurope"
--mi-system-assigned
--params "{
"logAnalyticsWorkspace": {
"value": "/subscriptions/$SUBSCRIPTION_ID/resourceGroups/monitoring-rg/providers/Microsoft.OperationalInsights/workspaces/central-logs"
}
}"
# Assignment ID'sini al
ASSIGNMENT_ID=$(az policy assignment show
--name "vm-diagnostics-auto-deploy"
--scope "/subscriptions/$SUBSCRIPTION_ID"
--query id -o tsv)
# Managed Identity'ye gerekli rolleri ver
PRINCIPAL_ID=$(az policy assignment show
--name "vm-diagnostics-auto-deploy"
--scope "/subscriptions/$SUBSCRIPTION_ID"
--query identity.principalId -o tsv)
az role assignment create
--assignee "$PRINCIPAL_ID"
--role "Contributor"
--scope "/subscriptions/$SUBSCRIPTION_ID"
echo "DeployIfNotExists policy hazır, principal ID: $PRINCIPAL_ID"
Initiative Oluşturma: Kurumsal Uyumluluk Paketi
Tek tek policy’ler atamak yerine, tüm güvenlik ve uyumluluk standartlarınızı bir initiative altında toplayın. Bu hem yönetimi kolaylaştırır hem de raporlamayı netleştirir.
# Initiative definition JSON oluştur
cat > corp-compliance-initiative.json << 'EOF'
{
"properties": {
"displayName": "Kurumsal Uyumluluk Standartları",
"description": "Şirket genelinde uygulanacak güvenlik ve operasyonel standartlar",
"policyDefinitions": [
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/1e30110a-5cfd-4ade-b895-3d14b50d1ac8",
"parameters": {
"tagName": {
"value": "CostCenter"
}
}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/1e30110a-5cfd-4ade-b895-3d14b50d1ac8",
"parameters": {
"tagName": {
"value": "Owner"
}
}
},
{
"policyDefinitionId": "/providers/Microsoft.Authorization/policyDefinitions/404c3081-a854-4457-ae30-26a93ef643f9",
"parameters": {}
}
],
"parameters": {}
}
}
EOF
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
# Initiative'i oluştur
az policy set-definition create
--name "corp-compliance-baseline"
--display-name "Kurumsal Uyumluluk Taban Çizgisi"
--definitions corp-compliance-initiative.json
--subscription $SUBSCRIPTION_ID
# Initiative'i subscription seviyesinde ata
az policy assignment create
--name "corp-compliance-assignment"
--display-name "Kurumsal Uyumluluk - Subscription Geneli"
--policy-set-definition "corp-compliance-baseline"
--scope "/subscriptions/$SUBSCRIPTION_ID"
--enforcement-mode Default
echo "Initiative başarıyla oluşturuldu ve atandı"
Uyumluluk Durumunu Sorgulama ve Raporlama
Policy’leri kurmak bir şey, mevcut durumu anlamak başka bir şey. Düzenli olarak uyumluluk raporları üretmeniz gerekiyor.
#!/bin/bash
# compliance-report.sh - Günlük uyumluluk raporu scripti
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
REPORT_DATE=$(date +%Y-%m-%d)
REPORT_FILE="compliance-report-$REPORT_DATE.txt"
echo "=== Azure Policy Uyumluluk Raporu ===" > $REPORT_FILE
echo "Tarih: $REPORT_DATE" >> $REPORT_FILE
echo "Subscription: $SUBSCRIPTION_ID" >> $REPORT_FILE
echo "" >> $REPORT_FILE
# Genel uyumluluk durumu
echo "--- Genel Uyumluluk Özeti ---" >> $REPORT_FILE
az policy state summarize
--subscription $SUBSCRIPTION_ID
--query "results.policyAssignments[].{
Policy:policyAssignmentId,
Compliant:results.resourceDetails[?complianceState=='Compliant'].count,
NonCompliant:results.resourceDetails[?complianceState=='NonCompliant'].count
}"
-o json >> $REPORT_FILE 2>&1
echo "" >> $REPORT_FILE
# Uyumsuz kaynakları listele
echo "--- Uyumsuz Kaynaklar ---" >> $REPORT_FILE
az policy state list
--subscription $SUBSCRIPTION_ID
--filter "complianceState eq 'NonCompliant'"
--query "[].{
Resource:resourceId,
Policy:policyDefinitionName,
Reason:policyDefinitionReferenceId
}"
-o table >> $REPORT_FILE 2>&1
echo "Rapor oluşturuldu: $REPORT_FILE"
# Kritik uyumsuzluk varsa mail at (sendmail kurulu olmalı)
NON_COMPLIANT_COUNT=$(az policy state list
--subscription $SUBSCRIPTION_ID
--filter "complianceState eq 'NonCompliant'"
--query "length(@)" -o tsv)
if [ "$NON_COMPLIANT_COUNT" -gt 10 ]; then
echo "UYARI: $NON_COMPLIANT_COUNT uyumsuz kaynak tespit edildi!"
# mail -s "Azure Policy Uyumluluk Uyarısı" [email protected] < $REPORT_FILE
fi
Remediation Task Oluşturma
Mevcut uyumsuz kaynakları düzeltmek için remediation task’ları oluşturmanız gerekir. Policy’yi atadığınızda sadece yeni kaynaklar değerlendirmeye alınır; mevcut kaynaklar için ayrıca remediation başlatmanız şart.
#!/bin/bash
# remediation.sh - Uyumsuz kaynakları otomatik düzelt
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
ASSIGNMENT_NAME="vm-diagnostics-auto-deploy"
# Remediation task oluştur
REMEDIATION_NAME="remediation-$(date +%Y%m%d-%H%M%S)"
az policy remediation create
--name "$REMEDIATION_NAME"
--policy-assignment "$ASSIGNMENT_NAME"
--resource-discovery-mode ReEvaluateCompliance
--subscription $SUBSCRIPTION_ID
echo "Remediation task başlatıldı: $REMEDIATION_NAME"
# Remediation durumunu takip et
while true; do
STATUS=$(az policy remediation show
--name "$REMEDIATION_NAME"
--subscription $SUBSCRIPTION_ID
--query "provisioningState" -o tsv)
echo "$(date): Remediation durumu: $STATUS"
if [ "$STATUS" = "Succeeded" ] || [ "$STATUS" = "Failed" ] || [ "$STATUS" = "Canceled" ]; then
break
fi
sleep 30
done
# Sonuç özeti
az policy remediation show
--name "$REMEDIATION_NAME"
--subscription $SUBSCRIPTION_ID
--query "{
Status:provisioningState,
Successful:deploymentSummary.successfulDeployments,
Failed:deploymentSummary.failedDeployments,
Total:deploymentSummary.totalDeployments
}" -o table
echo "Remediation tamamlandı"
Gerçek Dünya Senaryosu: Multi-Environment Uyumluluk Yönetimi
Tipik bir kurumsal senaryoyu ele alalım. Üç environment’ınız var: dev, staging ve production. Her birinin farklı uyumluluk gereksinimleri var.
Production’da:
- Tüm kaynaklar için Owner, CostCenter, Environment etiketi zorunlu
- Public IP adresi sadece onaylı resource group’lardan oluşturulabilir
- VM’ler sadece onaylı SKU’lardan seçilmeli
- Storage account’larda HTTPS zorunlu, public erişim yasak
Dev’de:
- Sadece Environment etiketi zorunlu
- Maliyet kontrolü için belirli VM SKU’larının üstüne çıkılamaz
#!/bin/bash
# multi-env-policy-setup.sh
SUBSCRIPTION_ID=$(az account show --query id -o tsv)
# Production resource group'u için sıkı policy paketi
PROD_RG="/subscriptions/$SUBSCRIPTION_ID/resourceGroups/production-rg"
DEV_RG="/subscriptions/$SUBSCRIPTION_ID/resourceGroups/development-rg"
# Production: Storage account public erişimi engelle
az policy assignment create
--name "prod-storage-no-public-access"
--display-name "[PROD] Storage Public Erişim Yasağı"
--policy "b2982f36-99f2-4db5-8eff-19bf0dcecc96"
--scope "$PROD_RG"
--enforcement-mode Default
# Production: Sadece belirli VM SKU'larına izin ver
cat > allowed-vm-skus.json << 'EOF'
{
"listOfAllowedSKUs": {
"value": [
"Standard_D2s_v3",
"Standard_D4s_v3",
"Standard_D8s_v3",
"Standard_E2s_v3",
"Standard_E4s_v3"
]
}
}
EOF
az policy assignment create
--name "prod-allowed-vm-skus"
--display-name "[PROD] İzin Verilen VM SKU'ları"
--policy "cccc23c7-8427-4f31-a4b3-13a3f6f8d77d"
--scope "$PROD_RG"
--params @allowed-vm-skus.json
--enforcement-mode Default
# Dev: Maliyet kontrolü için küçük SKU limiti
cat > dev-vm-skus.json << 'EOF'
{
"listOfAllowedSKUs": {
"value": [
"Standard_B1s",
"Standard_B2s",
"Standard_D2s_v3"
]
}
}
EOF
az policy assignment create
--name "dev-allowed-vm-skus"
--display-name "[DEV] İzin Verilen VM SKU'ları"
--policy "cccc23c7-8427-4f31-a4b3-13a3f6f8d77d"
--scope "$DEV_RG"
--params @dev-vm-skus.json
--enforcement-mode Default
echo "Multi-environment policy yapılandırması tamamlandı"
# Özet rapor
echo ""
echo "=== Policy Assignment Özeti ==="
az policy assignment list
--scope "/subscriptions/$SUBSCRIPTION_ID"
--query "[].{Name:name, DisplayName:displayName, Scope:scope}"
-o table
Policy Exceptions: İstisnalar Nasıl Yönetilir
Her kural katı uygulandığında meşru istisna ihtiyaçları ortaya çıkar. Azure Policy’nin exemption özelliği bu durumlar için kullanılır. Bir kaynağı kalıcı veya geçici olarak policy kapsamı dışında tutabilirsiniz.
# Belirli bir kaynak için exemption oluştur
# Örnek: Legacy uygulama sunucusu naming convention'a uyamıyor
az policy exemption create
--name "legacy-app-server-exemption"
--display-name "Legacy App Server İstisnası"
--exemption-category Waiver
--policy-assignment "/subscriptions/$(az account show --query id -o tsv)/providers/Microsoft.Authorization/policyAssignments/vm-naming-convention"
--resource "/subscriptions/$(az account show --query id -o tsv)/resourceGroups/production-rg/providers/Microsoft.Compute/virtualMachines/legacyappserver01"
--description "Bu sunucu 2025 Q2'de retire edilecek, istisnası o tarihe kadar geçerli"
--expires-on "2025-06-30T00:00:00Z"
echo "Exemption oluşturuldu ve 2025-06-30'da otomatik sona erecek"
# Mevcut exemption'ları listele
az policy exemption list
--scope "/subscriptions/$(az account show --query id -o tsv)"
--query "[].{Name:name, Category:exemptionCategory, Expires:expiresOn, Resource:id}"
-o table
Azure DevOps ile Policy as Code
Policy’leri portal veya CLI ile elle yönetmek başlangıç için iyidir ama gerçek otomasyon için policy’leri kod olarak versiyon kontrolüne alıp CI/CD pipeline’ına entegre etmek gerekir.
#!/bin/bash
# deploy-policies.sh - CI/CD pipeline için policy deployment scripti
# Bu script Azure DevOps pipeline'ında çalışacak şekilde tasarlandı
set -e # Herhangi bir hata olursa dur
SUBSCRIPTION_ID="${AZURE_SUBSCRIPTION_ID}"
ENVIRONMENT="${ENVIRONMENT:-dev}" # Pipeline değişkeninden gelir
POLICY_DIR="./policies"
echo "Deployment başlıyor: Environment=$ENVIRONMENT"
# Service principal ile login (pipeline service connection'dan gelir)
az login --service-principal
--username "$AZURE_CLIENT_ID"
--password "$AZURE_CLIENT_SECRET"
--tenant "$AZURE_TENANT_ID"
az account set --subscription "$SUBSCRIPTION_ID"
# Tüm policy definition'larını deploy et
for policy_file in $POLICY_DIR/definitions/*.json; do
POLICY_NAME=$(basename "$policy_file" .json)
echo "Policy deploy ediliyor: $POLICY_NAME"
az policy definition create
--name "$POLICY_NAME"
--rules "$policy_file"
--subscription "$SUBSCRIPTION_ID" 2>/dev/null ||
az policy definition update
--name "$POLICY_NAME"
--rules "$policy_file"
--subscription "$SUBSCRIPTION_ID"
echo "OK: $POLICY_NAME"
done
# Environment'a özel assignment'ları deploy et
ASSIGNMENT_FILE="$POLICY_DIR/assignments/$ENVIRONMENT.json"
if [ -f "$ASSIGNMENT_FILE" ]; then
echo "Assignment'lar yükleniyor: $ASSIGNMENT_FILE"
# JSON'dan assignment'ları oku ve oluştur
jq -c '.assignments[]' "$ASSIGNMENT_FILE" | while read assignment; do
NAME=$(echo "$assignment" | jq -r '.name')
POLICY=$(echo "$assignment" | jq -r '.policyDefinitionName')
SCOPE=$(echo "$assignment" | jq -r '.scope')
echo "Assignment oluşturuluyor: $NAME"
az policy assignment create
--name "$NAME"
--policy "$POLICY"
--scope "$SCOPE"
--enforcement-mode Default || echo "Assignment zaten mevcut: $NAME"
done
fi
echo "Deployment tamamlandı"
# Uyumluluk kontrolü - pipeline'ı başarısız saymak için
sleep 60 # Policy engine'in değerlendirmesi için bekle
NON_COMPLIANT=$(az policy state list
--subscription "$SUBSCRIPTION_ID"
--filter "complianceState eq 'NonCompliant'"
--query "length(@)" -o tsv)
echo "Uyumsuz kaynak sayısı: $NON_COMPLIANT"
if [ "$NON_COMPLIANT" -gt 0 ] && [ "$ENVIRONMENT" = "production" ]; then
echo "HATA: Production ortamında uyumsuz kaynaklar var!"
exit 1
fi
echo "Pipeline başarıyla tamamlandı"
Yaygın Hatalar ve Çözümleri
Policy effect’ini yanlış seçmek: Yeni bir policy’yi direkt Deny modunda production’a almak en yaygın hatadır. Her zaman önce Audit modunda çalıştırın, mevcut uyumsuz kaynakları görün, ekibi bilgilendirin, remediation planı yapın ve sonra Deny‘a geçin.
Scope’u yanlış belirlemek: Bir policy’yi subscription seviyesinde atayıp tüm resource group’ları etkilediğini fark etmemek ciddi sorunlara yol açar. Management group, subscription ve resource group scope’larını iyi anlayın.
Managed Identity rollerini unutmak: DeployIfNotExists veya Modify effect kullanan policy’ler için managed identity’ye uygun roller verilmezse remediation sessizce başarısız olur. Her zaman identity ve rol atamasını kontrol edin.
Policy evaluation lag’ini göz ardı etmek: Kaynak oluşturulduktan veya policy atandıktan sonra compliance state’in güncellenmesi 30 dakikayı bulabilir. “Policy çalışmıyor” diye paniklemeden önce bekleyin.
Sonuç
Azure Policy, büyük ölçekte Azure yönetiminin vazgeçilmez bir parçası. Bunu bir kez düzgün kurduğunuzda, “Bu kaynak neden böyle?” sorularına harcadığınız zamanı büyük ölçüde azaltıyorsunuz. Ekipler güvenli bir çerçeve içinde özgürce çalışabiliyor, siz de sürekli firefighting yapmak yerine gerçekten önemli işlere odaklanabiliyorsunuz.
Başlangıç için şu adımları öneririm: Önce bir test subscription’ında audit mode’da built-in policy’leri deneyin. Mevcut uyumsuzluk tablosuna bakın ve hangi sorunların öncelikli olduğunu belirleyin. Ekiple konuşun, zorunlu hale getireceğiniz kuralları paylaşın. Sonra kademeli olarak deny mode’a geçin.
Policy as Code yaklaşımını benimsemek ise uzun vadede en büyük kazanım. Policy definition’larınızı Git’e alın, değişiklikleri pull request süreciyle yönetin, deployment’ları pipeline üzerinden yapın. Bu yaklaşımla hem denetlenebilirlik kazanırsınız hem de “Kim bu policy’yi değiştirdi?” sorusuna anında cevap verebilirsiniz.
