Hello, Azure enthusiasts! I'm back again with another insightful blog post. My name is Felipe Binotto, Cloud Solution Architect, based in Australia.
Today, we're going to delve into a topic that is often overlooked but is critical to the smooth operation of your Azure environment - Azure Role Assignment Hygiene.
I will not cover guest user accounts as my colleague @Bruno Gabrielli has already covered it in detail in this great post.
Azure Role Assignment Hygiene refers to the practice of regularly reviewing and cleaning up Azure role assignments. This includes removing orphaned permissions, i.e., permissions that are no longer in use or are associated with non-existent users or groups. We are also going one step further and remove permissions for disabled users, including disabled users which are part of an Azure AD group or even a nested group!
Maintaining good Azure Role Assignment Hygiene is crucial for several reasons:
To achieve our goals, I will present you with two separate scripts. I recommend you use those scripts as Azure Automation runbooks and run them on a schedule to ensure you maintain a high level of role assignment hygiene.
The scripts will retrieve role assignments for every single Azure object, including Management Groups, Subscriptions, Resource Groups and Resources. Because I’m using an Azure Automation Account to execute those scripts, I added the identity of the Automation Account to the User Access Administrator role at the top Management Group scope. This role is required for both scripts. The second script also requires either the Azure AD Groups Administrator or User Administrator role.
The scripts were tested on PowerShell 5.1.
Let’s look at the scripts and understand how they work.
Pre-Requisites:
$null = Connect-AzAccount -Identity
$subscriptions = Get-AzSubscription | Where-Object State -eq "Enabled"
foreach($subscription in $subscriptions) {
Select-AzSubscription $subscription | Out-Null
Write-Output $subscription.Name
Get-AzRoleAssignment | Where-Object ObjectType -eq "Unknown" | Remove-AzRoleAssignment
}
$managementGroups = Get-AzManagementGroup
foreach($managementGroup in $managementGroups) {
Write-Output $managementGroup.Name
Get-AzRoleAssignment -Scope $managementGroup.id | Where-Object ObjectType -eq "Unknown" | Remove-AzRoleAssignment
}
The script does the following:
Pre-Requisites:
# Connect to Azure with user-assigned managed identity
$null = Connect-AzAccount -Identity
$subscriptions = Get-AzSubscription | Where-Object State -eq "Enabled"
$disabledUsers = Get-AzADUser -Filter "AccountEnabled eq false"
$permissions = @()
foreach($subscription in $subscriptions) {
Select-AzSubscription $subscription | Out-Null
Write-Output "Retrieving subscription, resource group and resource level permissions for subscription: $($subscription.Name)"
$permissions += Get-AzRoleAssignment
}
$managementGroups = Get-AzManagementGroup
foreach($managementGroup in $managementGroups) {
Write-Output "Retrieving management group level permissions for management group: $($managementGroup.Name)"
$permissions += Get-AzRoleAssignment -Scope $managementGroup.id
}
$userPermissions = $permissions | Where-Object ObjectType -eq "User" | Sort-Object roleAssignmentId -unique
$groupPermissions = $permissions | Where-Object ObjectType -eq "Group" | Sort-Object roleAssignmentId -unique
$allGroupsPermissions = $groupPermissions | Sort-Object ObjectId -unique
$countOfGroups = $allGroupsPermissions.count
$allUsersPermissions = $userPermissions | Sort-Object ObjectId -unique
foreach($permission in $allUsersPermissions){
if($permission.objectid -in $disabledUsers.id){
Remove-AzRoleAssignment -ObjectId $permission.ObjectId -Scope $permission.Scope -RoleDefinitionName $permission.RoleDefinitionName
Write-Output "Removing role assignment for user: $($permission.DisplayName) on scope: $($permission.Scope)"
}
}
function Get-AzureADGroupMemberNested($ObjectId) {
#Get the members of this group
$users = @()
$members = Get-AzADGroupMember -GroupObjectId $ObjectId -WarningAction Ignore
foreach ($member in $members) {
if ($member.ODataType -eq "#microsoft.graph.group" -and $member.id -ne $ObjectID) {
$skip = Get-AzADGroup -ObjectId $member.id
if($skip.AdditionalProperties.onPremisesLastSyncDateTime -or $skip.MembershipRule -or !$skip.SecurityEnabled){
Write-Output "Skipping group: $($skip.DisplayName) which is dynamic or synchronized or not security enabled."
}
else{
#If member is a group then recursively look at group membership
Get-AzureADGroupMemberNested -ObjectId $member.ID
}
}
elseif ($member.ODataType -eq "#microsoft.graph.user") {
$groupToQuery = @{}
$groupToQuery = $groupToQuery | Select-Object displayname, id, groupid
$groupToQuery.displayname = $member.DisplayName
$groupToQuery.id = $member.id
$groupToQuery.groupid = $ObjectID
$users += $groupToQuery
}
}
return $users
}
$count = 1
foreach($group in $allGroupsPermissions){
$skip = Get-AzADGroup -ObjectId $group.ObjectId
if($skip.AdditionalProperties.onPremisesLastSyncDateTime -or $skip.MembershipRule -or !$skip.SecurityEnabled){
Write-Output "Skipping group: $($skip.DisplayName) which is dynamic or synchronized or not security enabled."
}
else {
Write-Output "Querying AAD group: $($group.DisplayName) which is group $count out of $countOfGroups groups."
$groupToQuery = Get-AzureADGroupMemberNested -ObjectId $group.ObjectId
foreach($principalId in $groupToQuery){
if($principalId.id -in $disabledUsers.Id){
Remove-AzADGroupMember -MemberObjectId $principalId.id -GroupObjectId $principalId.groupid -WarningAction Ignore -WhatIf
Write-Output "Removing user: $($principalId.DisplayName) from AAD group: $($principalId.groupid)"
}
}
}
$count++
}
The script does the following:
Maintaining Azure Role Assignment Hygiene is a crucial aspect of managing your Azure environment. It helps to ensure security and compliance while simplifying the management process.
The scripts I've shared today are a powerful tool to automate the cleanup process. They remove orphaned permissions and disabled users, thereby reducing potential security risks. It's important to note that these scripts should be used as part of a broader strategy for Azure Role Assignment Hygiene, which includes regular reviews and audits of role assignments.
Remember, a clean Azure environment is a happy Azure environment!
Stay tuned for more posts on how to optimize your Azure experience. As always, feel free to leave any questions or comments below. Happy cloud computing!
I hope this was informative to you and thanks for reading!
Disclaimer
The sample scripts are not supported under any Microsoft standard support program or service. The sample scripts are provided AS IS without warranty of any kind. Microsoft further disclaims all implied warranties including, without limitation, any implied warranties of merchantability or of fitness for a particular purpose. The entire risk arising out of the use or performance of the sample scripts and documentation remains with you. In no event shall Microsoft, its authors, or anyone else involved in the creation, production, or delivery of the scripts be liable for any damages whatsoever (including, without limitation, damages for loss of business profits, business interruption, loss of business information, or other pecuniary loss) arising out of the use of or inability to use the sample scripts or documentation, even if Microsoft has been advised of the possibility of such damages.
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.