Azure Active Directory Grup ve Rol Yönetimi
Büyük bir kurumda onlarca departman, yüzlerce kullanıcı ve binlerce kaynak yönetiyorsanız, Azure Active Directory’deki grup ve rol yapısını doğru kurmazsanız hem güvenlik açığı hem de operasyonel kaos kaçınılmaz oluyor. Bunu yaşadım, çevremdeki birçok sysadmin de yaşadı. Bu yazıda sıfırdan enterprise seviyeye kadar Azure AD grup ve rol yönetimini, PowerShell ve Azure CLI örnekleriyle birlikte ele alacağız.
Azure AD Grup Türlerini Anlamak
Azure AD’de iki temel grup türü var: Security Groups ve Microsoft 365 Groups. Bunların altında da atama yöntemi açısından ikiye ayrılıyor: Assigned ve Dynamic.
Security Groups: Kaynaklara erişim vermek için kullanılır. Uygulama atamaları, Azure RBAC rolleri, Intune politikaları hep bu gruplar üzerinden yürür.
Microsoft 365 Groups: Teams, SharePoint, Exchange gibi iş birliği araçlarını kapsayan gruplar. Sadece erişim değil, ortak posta kutusu, Teams kanalı, SharePoint sitesi gibi yapılar da otomatik oluşur.
Assigned Groups: Üyeleri manuel olarak yönetirsiniz. Küçük takımlar veya özel projeler için uygundur.
Dynamic Groups: Azure AD özelliklerine göre (departman, iş unvanı, şehir vb.) üyelik otomatik belirlenir. Büyük organizasyonlarda hayat kurtarıcıdır.
Gerçek dünyada ne kullanılmalı? Genel kural şu: 50’den fazla kullanıcınız varsa Dynamic Groups olmadan yönetim eziyete dönüşüyor.
PowerShell ile Grup Oluşturma
Azure AD PowerShell modülü ile başlayalım. Önce bağlantı kuruyoruz:
# Azure AD modülünü yükle
Install-Module -Name AzureAD -Force -AllowClobber
# Microsoft Graph modülü (daha güncel ve önerilen)
Install-Module -Name Microsoft.Graph -Force
# Bağlantı kur
Connect-AzureAD
# veya Graph ile
Connect-MgGraph -Scopes "Group.ReadWrite.All", "Directory.ReadWrite.All"
Şimdi birkaç farklı senaryo için grup oluşturalım:
# Basit Security Group oluşturma
New-AzureADGroup `
-DisplayName "SG-Finance-ReadOnly" `
-Description "Finans departmanı okuma yetkili kullanıcılar" `
-MailEnabled $false `
-SecurityEnabled $true `
-MailNickName "SG-Finance-ReadOnly"
# Microsoft Graph ile güvenlik grubu oluşturma
$groupParams = @{
DisplayName = "SG-DevOps-Engineers"
Description = "DevOps mühendisleri güvenlik grubu"
MailEnabled = $false
SecurityEnabled = $true
MailNickname = "SG-DevOps-Engineers"
GroupTypes = @()
}
New-MgGroup @groupParams
Dynamic Group Kuralları ve Örnekleri
Dynamic grupların gücü, üyelik kurallarında yatıyor. Bir kez doğru kural yazarsanız HR sistemi üzerinden gelen değişiklikler anında gruplara yansıyor.
# Dinamik grup oluşturma - IT departmanındaki tüm kullanıcılar
$dynamicGroupParams = @{
DisplayName = "DynSG-IT-Department"
Description = "IT departmanı dinamik güvenlik grubu"
MailEnabled = $false
SecurityEnabled = $true
MailNickname = "DynSG-IT-Department"
GroupTypes = @("DynamicMembership")
MembershipRule = '(user.department -eq "Information Technology") and (user.accountEnabled -eq true)'
MembershipRuleProcessingState = "On"
}
New-MgGroup @dynamicGroupParams
# Daha karmaşık kural örneği - IT ve DevOps, yönetici olmayan kullanıcılar
$complexRule = '((user.department -eq "Information Technology") or (user.department -eq "DevOps")) and (user.jobTitle -ne "Manager") and (user.accountEnabled -eq true)'
# Yöneticiler için ayrı grup
$managerRule = '(user.department -eq "Information Technology") and (user.jobTitle -contains "Manager")'
Dynamic grup üyelik kurallarında dikkat etmeniz gereken birkaç nokta var:
- -eq: Tam eşleşme, büyük/küçük harf duyarsız
- -contains: Kısmi eşleşme
- -startsWith: Belirli bir değerle başlama
- -match: Regex desteği
- -in: Çoklu değer listesi ile karşılaştırma
Pratik bir senaryo: Şirketinizde Türkiye ofisindeki, aktif ve “Senior” ünvanlı tüm çalışanlar için bir grup oluşturmak istiyorsunuz:
$seniorTRRule = '(user.usageLocation -eq "TR") and (user.accountEnabled -eq true) and (user.jobTitle -startsWith "Senior")'
$seniorGroupParams = @{
DisplayName = "DynSG-Senior-TR"
Description = "Türkiye ofisi kıdemli çalışanlar"
MailEnabled = $false
SecurityEnabled = $true
MailNickname = "DynSG-Senior-TR"
GroupTypes = @("DynamicMembership")
MembershipRule = $seniorTRRule
MembershipRuleProcessingState = "On"
}
New-MgGroup @seniorGroupParams
Nested Group Yapısı ve Önemi
Azure AD’de nested (iç içe) grup yapısını doğru kullanmak, hem yönetimi kolaylaştırır hem de RBAC atamaları daha temiz hale gelir. Ancak şunu belirtmeliyim: Azure AD’de nested group desteği sınırlıdır. Bir güvenlik grubunu başka bir güvenlik grubuna üye yapabilirsiniz ama dinamik grupları nested olarak kullanamazsınız.
Örnek bir hiyerarşi kuralım:
# Ana departman grubu oluştur
$itMainGroup = New-MgGroup -DisplayName "SG-IT-All" `
-MailEnabled $false `
-SecurityEnabled $true `
-MailNickname "SG-IT-All" `
-Description "IT departmanı tüm kullanıcılar"
# Alt gruplar
$itOpsGroup = New-MgGroup -DisplayName "SG-IT-Operations" `
-MailEnabled $false `
-SecurityEnabled $true `
-MailNickname "SG-IT-Operations"
$itDevGroup = New-MgGroup -DisplayName "SG-IT-Development" `
-MailEnabled $false `
-SecurityEnabled $true `
-MailNickname "SG-IT-Development"
# Alt grupları ana gruba ekle (nested)
New-MgGroupMember -GroupId $itMainGroup.Id `
-DirectoryObjectId $itOpsGroup.Id
New-MgGroupMember -GroupId $itMainGroup.Id `
-DirectoryObjectId $itDevGroup.Id
Azure RBAC ile Grup Tabanlı Yetkilendirme
Burada çok kritik bir noktaya geliyoruz. Azure kaynakları üzerindeki yetkiler için bireysel kullanıcılara değil, her zaman gruplara RBAC rolü atamanız gerekiyor. Bunu kurallaştırın.
# Azure CLI ile resource group üzerinde gruba rol atama
az role assignment create
--assignee-object-id "GRUP_OBJECT_ID"
--assignee-principal-type Group
--role "Reader"
--scope "/subscriptions/SUBSCRIPTION_ID/resourceGroups/rg-production"
# Contributor rolü - belirli bir storage account için
az role assignment create
--assignee-object-id "GRUP_OBJECT_ID"
--assignee-principal-type Group
--role "Storage Blob Data Contributor"
--scope "/subscriptions/SUBSCRIPTION_ID/resourceGroups/rg-data/providers/Microsoft.Storage/storageAccounts/stproductiondata"
# Mevcut rol atamalarını listeleme
az role assignment list
--assignee "GRUP_OBJECT_ID"
--all
--output table
PowerShell ile aynı işlemi yapalım:
# PowerShell Az modülü ile rol atama
Connect-AzAccount
$groupObjectId = (Get-AzADGroup -DisplayName "SG-DevOps-Engineers").Id
# Subscription bazında Reader rolü
New-AzRoleAssignment `
-ObjectId $groupObjectId `
-RoleDefinitionName "Reader" `
-Scope "/subscriptions/$(Get-AzContext).Subscription.Id"
# Resource Group bazında Contributor
New-AzRoleAssignment `
-ObjectId $groupObjectId `
-RoleDefinitionName "Contributor" `
-Scope "/subscriptions/SUBSCRIPTION_ID/resourceGroups/rg-dev"
# Tüm rol atamalarını raporla
Get-AzRoleAssignment -ObjectId $groupObjectId | `
Select-Object RoleDefinitionName, Scope, DisplayName | `
Format-Table -AutoSize
Özel (Custom) Rol Tanımlama
Azure’un built-in rolleri çoğu senaryoyu karşılasa da bazen çok daha granüler yetkiler gerekebiliyor. Örneğin “sadece sanal makineyi başlatıp durdurabilsin ama silemesi” gibi bir senaryo.
# Custom rol tanımı JSON olarak oluştur
$customRoleDefinition = @{
Name = "VM Operator - Start Stop Only"
Description = "Sanal makineleri başlatıp durdurabilir ama oluşturamaz veya silemez"
Actions = @(
"Microsoft.Compute/virtualMachines/start/action",
"Microsoft.Compute/virtualMachines/deallocate/action",
"Microsoft.Compute/virtualMachines/restart/action",
"Microsoft.Compute/virtualMachines/read",
"Microsoft.Network/*/read",
"Microsoft.Resources/subscriptions/resourceGroups/read"
)
NotActions = @()
DataActions = @()
NotDataActions = @()
AssignableScopes = @(
"/subscriptions/SUBSCRIPTION_ID"
)
}
$customRoleJson = $customRoleDefinition | ConvertTo-Json -Depth 10
$customRoleJson | Out-File -FilePath "vm-operator-role.json" -Encoding UTF8
# Rolü oluştur
New-AzRoleDefinition -InputFile "vm-operator-role.json"
# Grubu bu özel role ata
$vmOpsGroup = Get-AzADGroup -DisplayName "SG-VM-Operators"
New-AzRoleAssignment `
-ObjectId $vmOpsGroup.Id `
-RoleDefinitionName "VM Operator - Start Stop Only" `
-ResourceGroupName "rg-production"
Privileged Identity Management Entegrasyonu
Azure AD P2 lisansınız varsa PIM (Privileged Identity Management) kullanmak artık bir tercih değil, zorunluluk. Just-in-time erişim, approval workflow ve erişim geçmişi gibi özellikler güvenliği dramatik ölçüde artırıyor.
PIM ile gruplara “uygun üye” (eligible member) atayabilirsiniz. Kullanıcı, o grupta sürekli değil, sadece ihtiyaç duyduğunda ve belirli bir süre için aktif olur.
# PIM ile grup ataması - Microsoft Graph API
# Önce PIM API'yi kullanmak için gerekli izinler
Connect-MgGraph -Scopes "PrivilegedAccess.ReadWrite.AzureADGroup"
# Kullanıcıyı gruba eligible member olarak ekle
$eligibleAssignment = @{
AccessId = "member"
PrincipalId = "KULLANICI_OBJECT_ID"
GroupId = "GRUP_OBJECT_ID"
Action = "AdminAssign"
ScheduleInfo = @{
StartDateTime = (Get-Date).ToString("yyyy-MM-ddTHH:mm:ssZ")
Expiration = @{
Type = "NoExpiration"
}
}
Justification = "DevOps engineer PIM eligible assignment"
}
New-MgIdentityGovernancePrivilegedAccessGroupEligibilityScheduleRequest `
-BodyParameter $eligibleAssignment
Grup Tabanlı Uygulama Ataması
Enterprise uygulamalarına erişimi de grup üzerinden yönetmek gerekiyor. Bunu yapmak hem güvenliği artırıyor hem de onboarding/offboarding süreçlerini otomatikleştiriyor.
# Azure CLI ile enterprise app'e grup atama
# Önce uygulamanın service principal ID'sini bul
az ad sp list --display-name "Salesforce" --query "[].{Name:displayName, Id:id}" -o table
# Gruba uygulama rolü ata
az ad group member add
--group "SG-Salesforce-Users"
--member-id "KULLANICI_OBJECT_ID"
# PowerShell ile uygulama grubu ataması
$app = Get-AzADServicePrincipal -DisplayName "Salesforce"
$group = Get-AzADGroup -DisplayName "SG-Salesforce-Users"
# App role assignment
$assignmentParams = @{
PrincipalId = $group.Id
ResourceId = $app.Id
AppRoleId = "00000000-0000-0000-0000-000000000000" # Default role ID
}
New-MgServicePrincipalAppRoleAssignedTo `
-ServicePrincipalId $app.Id `
-BodyParameter $assignmentParams
Büyük Ölçekli Grup Yönetimi ve Otomasyon
Yüzlerce grup yönetiyorsanız manuel işlemler yerine otomasyon şart. İşte gerçek hayatta kullandığım bir senaryo: HR sisteminden gelen CSV dosyasına göre grup üyeliklerini güncelleyen bir script.
# HR sisteminden gelen CSV'yi işle ve grup üyeliklerini senkronize et
# CSV formatı: UserPrincipalName,Department,JobTitle,Manager
param(
[Parameter(Mandatory=$true)]
[string]$CsvPath,
[Parameter(Mandatory=$false)]
[switch]$WhatIf
)
Connect-MgGraph -Scopes "Group.ReadWrite.All", "User.Read.All"
$hrData = Import-Csv -Path $CsvPath
# Departmana göre grup mapping
$departmentGroupMap = @{
"Information Technology" = "SG-IT-All"
"Finance" = "SG-Finance-All"
"Human Resources" = "SG-HR-All"
"Marketing" = "SG-Marketing-All"
}
foreach ($hrUser in $hrData) {
$user = Get-MgUser -Filter "userPrincipalName eq '$($hrUser.UserPrincipalName)'" -ErrorAction SilentlyContinue
if (-not $user) {
Write-Warning "Kullanıcı bulunamadı: $($hrUser.UserPrincipalName)"
continue
}
$targetGroupName = $departmentGroupMap[$hrUser.Department]
if (-not $targetGroupName) {
Write-Warning "Departman için grup tanımı yok: $($hrUser.Department)"
continue
}
$targetGroup = Get-MgGroup -Filter "displayName eq '$targetGroupName'"
# Kullanıcı zaten grupta mı kontrol et
$isMember = Get-MgGroupMember -GroupId $targetGroup.Id | Where-Object { $_.Id -eq $user.Id }
if (-not $isMember) {
if (-not $WhatIf) {
New-MgGroupMember -GroupId $targetGroup.Id -DirectoryObjectId $user.Id
Write-Host "Eklendi: $($user.DisplayName) -> $targetGroupName" -ForegroundColor Green
} else {
Write-Host "[WHATIF] Eklenecek: $($user.DisplayName) -> $targetGroupName" -ForegroundColor Yellow
}
}
}
Write-Host "Senkronizasyon tamamlandı." -ForegroundColor Cyan
Grup Raporlama ve Denetim
Hangi kullanıcı hangi grupta, hangi grubun hangi kaynağa erişimi var? Bu soruları dönemsel olarak cevaplandırmanız hem iç denetim hem de güvenlik açısından kritik.
# Tüm grupları ve üye sayılarını raporla
$allGroups = Get-MgGroup -All -Property "DisplayName,Description,GroupTypes,MembershipRule,CreatedDateTime"
$groupReport = foreach ($group in $allGroups) {
$memberCount = (Get-MgGroupMember -GroupId $group.Id -All).Count
$isDynamic = $group.GroupTypes -contains "DynamicMembership"
[PSCustomObject]@{
DisplayName = $group.DisplayName
Type = if ($isDynamic) { "Dynamic" } else { "Assigned" }
MemberCount = $memberCount
Created = $group.CreatedDateTime
Description = $group.Description
}
}
$groupReport | Sort-Object MemberCount -Descending | Format-Table -AutoSize
# CSV olarak dışa aktar
$groupReport | Export-Csv -Path "group-report-$(Get-Date -Format 'yyyyMMdd').csv" -Encoding UTF8 -NoTypeInformation
# Orphan grupları bul (0 üyeli, 90 günden eski gruplar)
$ninetyDaysAgo = (Get-Date).AddDays(-90)
$orphanGroups = $groupReport | Where-Object {
$_.MemberCount -eq 0 -and $_.Created -lt $ninetyDaysAgo
}
Write-Host "Boş gruplar (90+ gün): $($orphanGroups.Count)" -ForegroundColor Red
$orphanGroups | Format-Table DisplayName, Created
Sık Yapılan Hatalar ve Kaçınılması Gerekenler
Yıllar içinde gördüğüm ve kendim de düştüğüm tuzakları paylaşmak istiyorum:
Bireysel kullanıcılara doğrudan rol atamak: Her role assignment gruba yapılmalı. Kişi değiştiğinde grubu düzenliyorsunuz, Azure üzerinde izinleri tek tek aramıyorsunuz.
Anlamlı isimlendirme yapmamak: “Grup1”, “TestGrup” gibi isimler operasyonel felakete davet çıkarmak. Bir isimlendirme standardı belirleyin ve asla sapmayın. Örnek: SG-[Departman]-[Fonksiyon]-[ErisimSeviyesi]
Dynamic grup kurallarını test etmeden üretimde uygulamak: Azure portal üzerinde “Validate Rules” özelliği var, kesinlikle kullanın. Yanlış bir kural yüzlerce kullanıcının erişimini etkileyebilir.
Grup sahipliğini tanımlamamak: Her grubun mutlaka bir veya iki sahibi olmalı. Aksi halde IT deparmanı her değişiklik için tek adres haline gelir ve bu ölçeklenmiyor.
Geniş kapsamlı rolleri kullanmak: Subscription bazında “Owner” rolü sadece yöneticilere verilmeli. Geliştiricilere contributor bile çok geniş olabilir, custom rol tanımlayın.
PIM kullanmamak: P2 lisansınız varsa ve PIM kullanmıyorsanız, haklı bir güvenlik sorununu görmezden geliyorsunuz demektir.
Erişim Gözden Geçirme (Access Reviews)
Azure AD Access Reviews, belirli aralıklarla grup üyeliklerinin gözden geçirilmesini sağlar. Bu özellik hem GDPR hem de ISO 27001 gibi uyumluluk gereklilikleri için kritik.
# Microsoft Graph ile access review oluşturma
Connect-MgGraph -Scopes "AccessReview.ReadWrite.All"
$reviewParams = @{
DisplayName = "Finans Grubu Üyelik Gözden Geçirmesi - Q4 2024"
StartDateTime = "2024-10-01T00:00:00Z"
EndDateTime = "2024-10-31T00:00:00Z"
Scope = @{
Query = "/groups/FINANS_GRUP_ID/members"
QueryType = "MicrosoftGraph"
}
Reviewers = @(
@{
Query = "/users/FINANS_MUDUR_OBJECT_ID"
QueryType = "MicrosoftGraph"
}
)
Settings = @{
MailNotificationsEnabled = $true
ReminderNotificationsEnabled = $true
JustificationRequiredOnApproval = $true
DefaultDecisionEnabled = $true
DefaultDecision = "Deny"
InstanceDurationInDays = 30
AutoApplyDecisionsEnabled = $true
RecommendationsEnabled = $true
}
}
New-MgIdentityGovernanceAccessReviewDefinition -BodyParameter $reviewParams
Sonuç
Azure AD grup ve rol yönetimi, üzerine düşünülmeden kurulduğunda organizasyonun büyümesiyle birlikte giderek çözülmesi daha zor bir yapıya dönüşüyor. Baştan doğru temeli kurmak; anlamlı isimlendirme, dynamic gruplar, PIM entegrasyonu ve düzenli access review süreçleri hem güvenliği hem de operasyonel verimliliği köklü şekilde iyileştiriyor.
Benim önerim şu sırayla ilerlemek: Önce isimlendirme standardınızı belgeleyin. Sonra mevcut grupları bu standarda göre tarayın ve orphan grupları temizleyin. Ardından dynamic grupları devreye alın ve PIM’i aktive edin. Son adımda da erişim gözden geçirme süreçlerini takvime bağlayın.
Bu dört adımı tamamladığınızda hem denetçiniz hem de güvenlik ekibiniz size teşekkür edecek. Hepsinden önemlisi, bir güvenlik olayı yaşandığında “kim neye erişebilirdi” sorusuna dakikalar içinde cevap verebileceksiniz.
