Ensuring the security of your Azure resources is paramount. With the ever-evolving threat landscape and the increasing complexity of cloud deployments, maintaining visibility and control over access permissions is crucial for safeguarding sensitive data and preventing unauthorized actions.
Regularly auditing Azure role assignments is a fundamental aspect of maintaining a robust security posture in Azure environments. By reviewing and validating who has access to what resources, organizations can identify and mitigate potential security risks, comply with regulatory requirements, and maintain accountability across their cloud infrastructure.
However, auditing Azure role assignments manually via the portal can be a time-consuming and error-prone process, especially in large-scale deployments with numerous users and resources. This is where automation comes to the rescue.
To streamline the auditing process and empower Azure administrators with actionable insights, I’ve put together a simple PowerShell script that exports a comprehensive global list of all role assignments within your Azure environment.
This script provides administrators with a detailed overview of role assignments, including essential information such as what account has access, which role is assigned to them, whether the account is enabled, which Subscription and Scope the role is applied to, any description attached to the role assignment and any conditions.
By leveraging this script, Azure administrators can easily generate CSV reports containing critical details of role assignments, allowing for efficient auditing, analysis, and compliance tracking. Whether it’s for internal security reviews, compliance audits, or incident response, having a centralized and up-to-date record of role assignments is invaluable for maintaining the integrity and security of your Azure environment.
The Script
The script below will generate a CSV containing all accessible roles and details. Ensure you update the $subscriptionsToScan variable if you wish to limit the scope of the scan, update the $tenantId field to contain your own tenant ID and optionally change $removeInactiveAccess to $true which will attempt to remove any access assigned to inactive accounts.
# Returns a list of resource role allocations
$removeInactiveAccess = $false # If enabled, removes access for inactive accounts
$subscriptionsToScan = @() # Define a list of Subscriptions by name to search, if nothing is defined then all available Subscriptions are searched
$tenantId = '#####'
Connect-AzAccount -TenantId $tenantId
# Retrieve user and service principals to determine whether enabled
$allUserDetails = Get-AZADUser -select Id, AccountEnabled
$allServicePrincipals = Get-AzADServicePrincipal -select Id, AccountEnabled
# Loop through each available subscription
$allRoleAssignments = @()
$subscriptions = Get-AzSubscription
foreach($subscription in $subscriptions) {
if($null -ne $subscriptionsToScan -and $subscriptionsToScan.length -ne 0 -and !($subscriptionsToScan.Contains($subscription.Name))) {
Write-Host "Skipped $($subscription.Name) due to filter"
continue
}
Select-AzSubscription -subscriptionId $subscription.Id | Out-Null
Write-Host "Processing $($subscription.Name)"
# Loop through all Role Assignments
$roleAssignments = Get-AZRoleAssignment
foreach ($assignment in $roleAssignments) {
# Determine the type of resource
$scopeChars = ([regex]::Matches($assignment.Scope, "/" )).count
if($scopeChars -eq 1) {
$scopeType = "Global"
} elseif($scopeChars -eq 2) {
$scopeType = "Subscription"
} elseif($assignment.Scope -eq "/providers/Microsoft.Management/managementGroups/Group") {
$scopeType = "Management Group"
} elseif($scopeChars -eq 4) {
$scopeType = "Resource Group"
} else {
$scopeType = "Resource"
}
$assignment | Add-Member -MemberType NoteProperty -Name "ScopeType" -Value $scopeType
## Add the Subscription Name
$assignment | Add-Member -MemberType NoteProperty -Name "Subscription" -Value $subscription.Name
# Add account status
if($assignment.ObjectType -eq "User") {
$recordDetails = $allUserDetails | Where-Object {$_.Id -eq $assignment.ObjectId}
$assignment | Add-Member -MemberType NoteProperty -Name "AccountEnabled" -Value $recordDetails.AccountEnabled
} elseif($assignment.ObjectType -eq "ServicePrincipal") {
$recordDetails = $allServicePrincipals | Where-Object {$_.Id -eq $assignment.ObjectId}
$assignment | Add-Member -MemberType NoteProperty -Name "AccountEnabled" -Value $recordDetails.AccountEnabled
} elseif($assignment.ObjectType -eq "Group") {
$assignment | Add-Member -MemberType NoteProperty -Name "AccountEnabled" -Value $true
} else {
$assignment | Add-Member -MemberType NoteProperty -Name "AccountEnabled" -Value $false
}
# Remove access if granted to inactive user
if($removeInactiveAccess -eq $true -and $assignment.AccountEnabled -eq $false) {
Remove-AzRoleAssignment $assignment | Out-Null
$assignment | Add-Member -MemberType NoteProperty -Name "Removed" -Value $true
Write-Host "Removed $($assignment.RoleDefinitionName) role assigned to $($userDetails.DisplayName) in $($subscription.Name)"
continue
} else {
$assignment | Add-Member -MemberType NoteProperty -Name "Removed" -Value $false
}
}
$allRoleAssignments += $roleAssignments
}
# Export the results into a CSV then open the CSV
$date = Get-Date
$allRoleAssignments |
Select-Object ObjectId, DisplayName, ObjectType, SignInName, AccountEnabled, RoleDefinitionName, Subscription, Description, ConditionVersion, Condition, Scope, ScopeType |
export-csv -NoClobber -NoTypeInformation "C:\temp\report-$($date.Year)$($date.Month)$($date.Day)$($date.Millisecond).csv"
Write-Host "Script complete. Opening report."
Invoke-Item "C:\temp\report-$($date.Year)$($date.Month)$($date.Day)$($date.Millisecond).csv"
Disconnect-AzAccount








Leave a comment