mirror of
https://dev.azure.com/effectory/Survey%20Software/_git/Cloud%20Engineering
synced 2026-02-27 10:45:02 +01:00
427 lines
23 KiB
PowerShell
427 lines
23 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Comprehensive Azure RBAC assignment analysis across entire Azure tenant.
|
|
|
|
.DESCRIPTION
|
|
This script analyzes RBAC assignments across all Azure resources in an Azure tenant,
|
|
providing a complete inventory of role assignments at every level of the Azure
|
|
resource hierarchy. The script generates a detailed CSV report with comprehensive
|
|
metadata including resource information, role assignments, and organizational tags.
|
|
|
|
Features:
|
|
• Recursive analysis of Management Groups, Subscriptions, Resource Groups, and Resources
|
|
• Complete RBAC assignment enumeration with role and principal details
|
|
• Organizational metadata collection (tags, locations, resource types)
|
|
• Hierarchical resource context (management group → subscription → resource group → resource)
|
|
• Timestamped CSV output for historical tracking and compliance reporting
|
|
• Comprehensive error handling to continue processing despite individual failures
|
|
|
|
.PARAMETER SubscriptionIds
|
|
Optional array of specific subscription IDs to process. If not provided, all active
|
|
subscriptions across all management groups will be processed. When specified, only
|
|
the listed subscriptions will be analyzed for RBAC assignments.
|
|
|
|
.PARAMETER OutputPath
|
|
Optional custom path for the output CSV file. If not provided, the file will be
|
|
created in the current directory with a timestamped filename.
|
|
|
|
.EXAMPLE
|
|
.\AzureRBAC.ps1
|
|
Process all management groups and subscriptions to generate complete RBAC inventory.
|
|
|
|
.EXAMPLE
|
|
.\AzureRBAC.ps1 -SubscriptionIds @("a134faf1-7a89-4f2c-8389-06d00bd5e2a7", "30ce4e64-4299-4b93-91b8-4c953f63678e")
|
|
Process only the specified subscriptions for RBAC analysis.
|
|
|
|
.EXAMPLE
|
|
.\AzureRBAC.ps1 -SubscriptionIds @("12345678-1234-1234-1234-123456789012") -OutputPath "C:\Reports\rbac-analysis.csv"
|
|
Process a specific subscription and save results to a custom location.
|
|
|
|
.OUTPUTS
|
|
CSV file: [YYYY-MM-DD HHMM] azure_rbac_assignments.csv
|
|
|
|
The output file contains the following columns:
|
|
- ResourceId: Azure resource identifier
|
|
- Id: Resource ID (for individual resources)
|
|
- Kind: Resource type (ManagementGroup, Subscription, ResourceGroup, Resource)
|
|
- Location: Azure region/location
|
|
- ResourceName: Name of the resource
|
|
- ResourceGroupName: Resource group containing the resource
|
|
- ResourceType: Azure resource type (e.g., Microsoft.Storage/storageAccounts)
|
|
- ManagementGroupId: Parent management group identifier
|
|
- ManagementGroupName: Parent management group display name
|
|
- SubscriptionId: Subscription identifier
|
|
- SubscriptionName: Subscription display name
|
|
- Tag_Team: Team tag value for organizational tracking
|
|
- Tag_Product: Product tag value for product alignment
|
|
- Tag_Environment: Environment tag value (Dev, Test, Prod, etc.)
|
|
- Tag_Data: Data classification tag value
|
|
- Tag_Delete: Deletion schedule tag value
|
|
- Tag_Split: Cost allocation split tag value
|
|
- RBAC_RoleAssignmentId: Unique role assignment identifier
|
|
- RBAC_Scope: Scope where the role assignment is effective
|
|
- RBAC_DisplayName: Display name of the assigned principal
|
|
- RBAC_SignInName: Sign-in name/UPN of the assigned principal
|
|
- RBAC_RoleDefinitionName: Name of the assigned Azure role
|
|
|
|
.NOTES
|
|
Requires PowerShell modules: Az.Accounts, Az.Resources
|
|
|
|
Requires appropriate Azure RBAC permissions:
|
|
• Reader access or higher on Management Groups
|
|
• Reader access or higher on Subscriptions
|
|
• Reader access or higher on Resource Groups and Resources
|
|
• Microsoft.Authorization/roleAssignments/read permission at appropriate scopes
|
|
|
|
Authentication:
|
|
• Must be authenticated to Azure (Connect-AzAccount) before running
|
|
• Service Principal or Managed Identity authentication supported
|
|
• Requires appropriate tenant-level permissions for comprehensive analysis
|
|
|
|
Performance Considerations:
|
|
• Processing time scales with number of resources and role assignments
|
|
• Large tenants may require several minutes to hours for complete analysis
|
|
• Network connectivity and API throttling may affect processing speed
|
|
• Memory usage scales with number of resources processed
|
|
|
|
Compatibility:
|
|
• PowerShell 5.1 and PowerShell 7.x
|
|
• Azure PowerShell module version 8.0 or later
|
|
• Windows, macOS, and Linux support
|
|
|
|
Output Management:
|
|
• CSV files are appended to support incremental data collection
|
|
• Timestamped filenames prevent overwrites
|
|
• Large result sets may generate substantial file sizes
|
|
• Consider data retention and storage management policies
|
|
|
|
.LINK
|
|
https://docs.microsoft.com/en-us/azure/role-based-access-control/
|
|
https://docs.microsoft.com/en-us/powershell/azure/
|
|
|
|
AUTHOR: Cloud Engineering Team
|
|
CREATED: Azure governance and compliance toolkit
|
|
VERSION: 2.0 - Simplified RBAC analysis without PIM detection
|
|
UPDATED: Focused on core RBAC assignment enumeration and organizational metadata
|
|
#>
|
|
|
|
# Script parameters
|
|
param(
|
|
[Parameter(Mandatory = $false, HelpMessage = "Array of subscription IDs to process. If not specified, all subscriptions will be processed.")]
|
|
[string[]]$SubscriptionIds = @(),
|
|
|
|
[Parameter(Mandatory = $false, HelpMessage = "Custom output path for the CSV file. If not specified, uses timestamped filename in current directory.")]
|
|
[string]$OutputPath = ""
|
|
)
|
|
|
|
#Connect-AzAccount
|
|
Import-Module Az.Accounts
|
|
Import-Module Az.Resources
|
|
|
|
# PowerShell class to represent Azure resource and RBAC assignment data
|
|
class ResourceCheck {
|
|
# Resource identification and metadata
|
|
[string] $ResourceId = "" # Azure resource identifier (full ARM path)
|
|
[string] $Id = "" # Resource ID (used for individual resources)
|
|
[string] $Kind = "" # Resource level (ManagementGroup, Subscription, ResourceGroup, Resource)
|
|
[string] $Location = "" # Azure region/location where resource is deployed
|
|
[string] $ResourceName = "" # Name of the resource
|
|
[string] $ResourceGroupName = "" # Resource group containing the resource
|
|
[string] $ResourceType = "" # Azure resource type (e.g., Microsoft.Storage/storageAccounts)
|
|
|
|
# Organizational hierarchy context
|
|
[string] $ManagementGroupId = "" # Parent management group identifier
|
|
[string] $ManagementGroupName = "" # Parent management group display name
|
|
[string] $SubscriptionId = "" # Subscription identifier
|
|
[string] $SubscriptionName = "" # Subscription display name
|
|
|
|
# Organizational metadata tags (customize based on organizational tagging strategy)
|
|
[string] $Tag_Team = "" # Team responsible for the resource
|
|
[string] $Tag_Product = "" # Product/application alignment
|
|
[string] $Tag_Environment = "" # Environment classification (Dev, Test, Prod, etc.)
|
|
[string] $Tag_Data = "" # Data classification level
|
|
[string] $Tag_Delete = "" # Scheduled deletion information
|
|
[string] $Tag_Split = "" # Cost allocation split information
|
|
|
|
# RBAC assignment details
|
|
[string] $RBAC_RoleAssignmentId = "" # Unique identifier for the role assignment
|
|
[string] $RBAC_Scope = "" # Scope where the role assignment is effective
|
|
[string] $RBAC_DisplayName = "" # Display name of the assigned principal
|
|
[string] $RBAC_SignInName = "" # Sign-in name/UPN of the assigned principal
|
|
[string] $RBAC_RoleDefinitionName = "" # Name of the assigned Azure role
|
|
}
|
|
|
|
# Display script execution banner
|
|
Write-Host "========================================================================================================================================================================"
|
|
Write-Host "Creating resource RBAC assignment overview."
|
|
Write-Host "========================================================================================================================================================================"
|
|
Write-Host "Azure RBAC Assignment Analysis"
|
|
Write-Host "========================================================================================================================================================================"
|
|
|
|
# Display processing scope information
|
|
if ($SubscriptionIds.Count -gt 0) {
|
|
Write-Host "SCOPE: Processing specific subscriptions only"
|
|
Write-Host "Subscription IDs to process:"
|
|
foreach ($subId in $SubscriptionIds) {
|
|
Write-Host " - $subId"
|
|
}
|
|
} else {
|
|
Write-Host "SCOPE: Processing ALL active subscriptions across all management groups"
|
|
}
|
|
Write-Host ""
|
|
|
|
# Generate filename for output CSV (use custom path or default timestamped filename)
|
|
if ($OutputPath -ne "") {
|
|
$fileName = $OutputPath
|
|
Write-Host "Using custom output file: $fileName"
|
|
} else {
|
|
[string] $date = Get-Date -Format "yyyy-MM-dd HHmm"
|
|
$fileName = ".\$date azure_rbac_assignments.csv"
|
|
Write-Host "Using default timestamped filename: $fileName"
|
|
}
|
|
|
|
# Discover all management groups in the tenant
|
|
# This provides the top-level organizational structure for Azure resources
|
|
$managementGroups = Get-AzManagementGroup
|
|
|
|
# Process each management group in the tenant
|
|
foreach ($managementGroup in $managementGroups)
|
|
{
|
|
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
|
|
Write-Host "Management group [$($managementGroup.Name)]"
|
|
|
|
# Initialize collection for management group level role assignments
|
|
[ResourceCheck[]]$Result = @()
|
|
|
|
try {
|
|
# Get role assignments directly assigned to this management group (not inherited)
|
|
$roleAssignments = Get-AzRoleAssignment -Scope $managementGroup.Id | Where-Object Scope -eq $managementGroup.Id
|
|
|
|
# Process each role assignment at the management group level
|
|
foreach($roleAssignment in $roleAssignments) {
|
|
[ResourceCheck] $resourceCheck = [ResourceCheck]::new()
|
|
|
|
# Set management group context (no individual resource context at this level)
|
|
$resourceCheck.ResourceId = "" # No specific resource for MG assignments
|
|
$resourceCheck.Kind = "ManagementGroup" # Indicates this is a management group level assignment
|
|
$resourceCheck.Location = "" # Management groups don't have locations
|
|
$resourceCheck.ResourceGroupName = "" # No resource group context
|
|
$resourceCheck.ManagementGroupId = $managementGroup.Id
|
|
$resourceCheck.ManagementGroupName = $managementGroup.DisplayName
|
|
$resourceCheck.SubscriptionId = "" # No subscription context at MG level
|
|
$resourceCheck.SubscriptionName = ""
|
|
|
|
# Management groups don't have tags in the same way resources do
|
|
$resourceCheck.Tag_Team = ""
|
|
$resourceCheck.Tag_Product = ""
|
|
$resourceCheck.Tag_Environment = ""
|
|
$resourceCheck.Tag_Data = ""
|
|
$resourceCheck.Tag_Delete = ""
|
|
$resourceCheck.Tag_Split = ""
|
|
|
|
# Populate RBAC assignment details
|
|
$resourceCheck.RBAC_RoleAssignmentId = $roleAssignment.RoleAssignmentId
|
|
$resourceCheck.RBAC_Scope = $roleAssignment.Scope
|
|
$resourceCheck.RBAC_DisplayName = $roleAssignment.DisplayName
|
|
$resourceCheck.RBAC_SignInName = $roleAssignment.SignInName
|
|
$resourceCheck.RBAC_RoleDefinitionName = $roleAssignment.RoleDefinitionName
|
|
|
|
$Result += $resourceCheck
|
|
}
|
|
} catch {
|
|
}
|
|
$Result | Export-Csv -Path $fileName -Append -NoTypeInformation
|
|
|
|
|
|
# Get all active subscriptions under this management group
|
|
$allSubscriptions = Get-AzManagementGroupSubscription -Group $managementGroup.Name | Where-Object State -eq "Active"
|
|
|
|
# Filter subscriptions if specific subscription IDs were provided
|
|
if ($SubscriptionIds.Count -gt 0) {
|
|
$subscriptions = $allSubscriptions | Where-Object {
|
|
$subscriptionId = $_.Id.Split('/')[-1] # Extract subscription ID from resource path
|
|
$subscriptionId -in $SubscriptionIds
|
|
}
|
|
if ($subscriptions.Count -eq 0) {
|
|
Write-Host "No matching subscriptions found in management group '$($managementGroup.DisplayName)' for the specified subscription IDs."
|
|
continue
|
|
}
|
|
Write-Host "Processing $($subscriptions.Count) filtered subscription(s) in management group '$($managementGroup.DisplayName)'"
|
|
} else {
|
|
$subscriptions = $allSubscriptions
|
|
Write-Host "Processing all $($subscriptions.Count) active subscription(s) in management group '$($managementGroup.DisplayName)'"
|
|
}
|
|
|
|
# Process each subscription under the current management group
|
|
foreach ($subscription in $subscriptions)
|
|
{
|
|
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
|
|
|
|
# Extract subscription ID from the full resource path
|
|
$scope = $subscription.Id.Substring($subscription.Parent.Length, $subscription.Id.Length - $subscription.Parent.Length)
|
|
$subscriptionId = $scope.Replace("/subscriptions/", "")
|
|
Write-Host "Subscription [$($subscription.DisplayName) - $subscriptionId]"
|
|
|
|
# Set Azure PowerShell context to the current subscription
|
|
Set-AzContext -SubscriptionId $subscriptionId | Out-Null
|
|
Write-Host "----------------------------------------------------------------------------------------------------------------------------------------------------------------------"
|
|
|
|
# Initialize result array for this subscription's role assignments
|
|
[ResourceCheck[]]$Result = @()
|
|
|
|
# Get role assignments directly assigned to this subscription scope
|
|
try {
|
|
$roleAssignments = Get-AzRoleAssignment -Scope $scope | Where-Object Scope -eq $scope
|
|
|
|
# Process each subscription-level role assignment
|
|
foreach($roleAssignment in $roleAssignments) {
|
|
[ResourceCheck] $resourceCheck = [ResourceCheck]::new()
|
|
|
|
# Set subscription context (no individual resource context at this level)
|
|
$resourceCheck.ResourceId = "" # No specific resource for subscription assignments
|
|
$resourceCheck.Kind = "Subscription" # Indicates this is a subscription level assignment
|
|
$resourceCheck.Location = "" # Subscriptions don't have specific locations
|
|
$resourceCheck.ResourceGroupName = "" # No resource group context at subscription level
|
|
$resourceCheck.ManagementGroupId = $managementGroup.Id
|
|
$resourceCheck.ManagementGroupName = $managementGroup.DisplayName
|
|
$resourceCheck.SubscriptionId = $subscription.Id
|
|
$resourceCheck.SubscriptionName = $subscription.DisplayName
|
|
# Extract subscription-level tags if available
|
|
$resourceCheck.Tag_Team = $subscription.Tags.team
|
|
$resourceCheck.Tag_Product = $subscription.Tags.product
|
|
$resourceCheck.Tag_Environment = $subscription.Tags.environment
|
|
$resourceCheck.Tag_Data = $subscription.Tags.data
|
|
$resourceCheck.Tag_Delete = $subscription.Tags.delete
|
|
$resourceCheck.Tag_Split = $subscription.Tags.split
|
|
|
|
# Populate RBAC assignment details
|
|
$resourceCheck.RBAC_RoleAssignmentId = $roleAssignment.RoleAssignmentId
|
|
$resourceCheck.RBAC_Scope = $roleAssignment.Scope
|
|
$resourceCheck.RBAC_DisplayName = $roleAssignment.DisplayName
|
|
$resourceCheck.RBAC_SignInName = $roleAssignment.SignInName
|
|
$resourceCheck.RBAC_RoleDefinitionName = $roleAssignment.RoleDefinitionName
|
|
|
|
$Result += $resourceCheck
|
|
}
|
|
} catch {
|
|
# Silently handle any errors during subscription-level role assignment processing
|
|
}
|
|
# Export subscription-level role assignments to CSV
|
|
$Result | Export-Csv -Path $fileName -Append -NoTypeInformation
|
|
|
|
# Get all resource groups within the current subscription
|
|
$resourceGroups = Get-AzResourceGroup
|
|
|
|
# Process each resource group for RBAC assignments
|
|
foreach ($resourceGroup in $resourceGroups) {
|
|
|
|
# Initialize result array for this resource group's role assignments
|
|
[ResourceCheck[]]$Result = @()
|
|
|
|
# Get role assignments at resource group level and below
|
|
try {
|
|
$roleAssignments = Get-AzRoleAssignment -Scope $resourceGroup.ResourceId | Where-Object Scope -Like "$($resourceGroup.ResourceId)*"
|
|
|
|
# Process each resource group-level role assignment
|
|
foreach($roleAssignment in $roleAssignments) {
|
|
[ResourceCheck] $resourceCheck = [ResourceCheck]::new()
|
|
# Set resource group context
|
|
$resourceCheck.ResourceId = $resourceGroup.ResourceId
|
|
$resourceCheck.Kind = "ResourceGroup" # Indicates this is a resource group level assignment
|
|
$resourceCheck.Location = $resourceGroup.Location
|
|
$resourceCheck.ResourceGroupName = $resourceGroup.ResourceGroupName
|
|
$resourceCheck.ManagementGroupId = $managementGroup.Id
|
|
$resourceCheck.ManagementGroupName = $managementGroup.DisplayName
|
|
$resourceCheck.SubscriptionId = $subscription.Id
|
|
$resourceCheck.SubscriptionName = $subscription.DisplayName
|
|
|
|
# Extract resource group-level tags if available
|
|
$resourceCheck.Tag_Team = $resourceGroup.Tags.team
|
|
$resourceCheck.Tag_Product = $resourceGroup.Tags.product
|
|
$resourceCheck.Tag_Environment = $resourceGroup.Tags.environment
|
|
$resourceCheck.Tag_Data = $resourceGroup.Tags.data
|
|
$resourceCheck.Tag_Delete = $resourceGroup.Tags.delete
|
|
$resourceCheck.Tag_Split = $resourceGroup.Tags.split
|
|
|
|
# Populate RBAC assignment details
|
|
$resourceCheck.RBAC_RoleAssignmentId = $roleAssignment.RoleAssignmentId
|
|
$resourceCheck.RBAC_Scope = $roleAssignment.Scope
|
|
$resourceCheck.RBAC_DisplayName = $roleAssignment.DisplayName
|
|
$resourceCheck.RBAC_SignInName = $roleAssignment.SignInName
|
|
$resourceCheck.RBAC_RoleDefinitionName = $roleAssignment.RoleDefinitionName
|
|
|
|
$Result += $resourceCheck
|
|
}
|
|
} catch {
|
|
# Silently handle any errors during resource group-level role assignment processing
|
|
}
|
|
# Export resource group-level role assignments to CSV
|
|
$Result | Export-Csv -Path $fileName -Append -NoTypeInformation
|
|
}
|
|
|
|
# Get all individual resources within the current subscription
|
|
$allResources = Get-AzResource
|
|
|
|
# Process each individual resource for RBAC assignments
|
|
foreach ($resource in $allResources) {
|
|
|
|
# Initialize result array for this resource's role assignments
|
|
[ResourceCheck[]]$Result = @()
|
|
|
|
# Get role assignments directly assigned to this specific resource
|
|
try {
|
|
$roleAssignments = Get-AzRoleAssignment -Scope $resource.ResourceId | Where-Object Scope -eq $resource.ResourceId
|
|
|
|
# Process each resource-level role assignment
|
|
foreach($roleAssignment in $roleAssignments) {
|
|
[ResourceCheck] $resourceCheck = [ResourceCheck]::new()
|
|
|
|
# Set individual resource context with full metadata
|
|
$resourceCheck.ResourceId = $resource.ResourceId
|
|
$resourceCheck.Id = $resource.Id # Additional resource identifier
|
|
$resourceCheck.Kind = "Resource" # Indicates this is an individual resource assignment
|
|
$resourceCheck.Location = $resource.Location
|
|
$resourceCheck.ResourceName = $resource.ResourceName
|
|
$resourceCheck.ResourceGroupName = $resource.ResourceGroupName
|
|
$resourceCheck.ResourceType = $resource.ResourceType # e.g., Microsoft.Storage/storageAccounts
|
|
$resourceCheck.ManagementGroupId = $managementGroup.Id
|
|
$resourceCheck.ManagementGroupName = $managementGroup.DisplayName
|
|
$resourceCheck.SubscriptionId = $subscription.Id
|
|
$resourceCheck.SubscriptionName = $subscription.DisplayName
|
|
|
|
# Extract resource-level tags if available
|
|
$resourceCheck.Tag_Team = $resource.Tags.team
|
|
$resourceCheck.Tag_Product = $resource.Tags.product
|
|
$resourceCheck.Tag_Environment = $resource.Tags.environment
|
|
$resourceCheck.Tag_Data = $resource.Tags.data
|
|
$resourceCheck.Tag_Delete = $resource.Tags.delete
|
|
$resourceCheck.Tag_Split = $resource.Tags.split
|
|
|
|
# Populate RBAC assignment details
|
|
$resourceCheck.RBAC_RoleAssignmentId = $roleAssignment.RoleAssignmentId
|
|
$resourceCheck.RBAC_Scope = $roleAssignment.Scope
|
|
$resourceCheck.RBAC_DisplayName = $roleAssignment.DisplayName
|
|
$resourceCheck.RBAC_SignInName = $roleAssignment.SignInName
|
|
$resourceCheck.RBAC_RoleDefinitionName = $roleAssignment.RoleDefinitionName
|
|
|
|
$Result += $resourceCheck
|
|
}
|
|
} catch {
|
|
# Silently handle any errors during individual resource-level role assignment processing
|
|
}
|
|
# Export individual resource-level role assignments to CSV
|
|
$Result | Export-Csv -Path $fileName -Append -NoTypeInformation
|
|
}
|
|
}
|
|
}
|
|
|
|
# Final completion message
|
|
Write-Host "========================================================================================================================================================================"
|
|
if ($SubscriptionIds.Count -gt 0) {
|
|
Write-Host "RBAC analysis complete for $($SubscriptionIds.Count) specified subscription(s)."
|
|
} else {
|
|
Write-Host "RBAC analysis complete for all active subscriptions across all management groups."
|
|
}
|
|
Write-Host "Results exported to: $fileName"
|
|
Write-Host "Done."
|