Entra / Microsoft 365 · Conditional Access
Disable Exchange PowerShell for non-admins
Restricts Exchange Online PowerShell access to mailboxes owned by Exchange and Global administrators identified through PIM role assignments.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
Connect-MgGraph -Scopes RoleAssignmentSchedule.Read.Directory, RoleEligibilitySchedule.Read.Directory, User.Read.All, Group.Read.All, GroupMember.Read.All -NoWelcome
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
Connect-MgGraph -Scopes RoleAssignmentSchedule.Read.Directory, RoleEligibilitySchedule.Read.Directory, User.Read.All, Group.Read.All, GroupMember.Read.All -NoWelcome$Modules = Get-Module | Select-Object -ExpandProperty NameIf ($Modules -notcontains "ExchangeOnlineManagement") {Connect-ExchangeOnline}# Find the identifiers for the Exchange administrator and Global administrator management roles$ExoAdminRoleId = Get-MgDirectoryRoleTemplate | Where-Object {$_.displayName -eq "Exchange administrator"} | Select-Object -ExpandProperty Id$GlobalAdminRoleId = Get-MgDirectoryRoleTemplate | Where-Object {$_.displayName -eq "Global administrator"} | Select-Object -ExpandProperty Id# Output list to gather information about admin accounts$Report = [System.Collections.Generic.List[Object]]::new()$OutputFile = "c:\temp\PIMAssignments.csv"Write-Output "Retrieving assignment information from Privileged Identity Management..."# Get PIM assignments for accounts holding Exchange administrator or Global administrator roles[array]$ActiveAssignments = Get-MgBetaRoleManagementDirectoryRoleAssignmentSchedule -Filter "(RoleDefinitionId eq '$($ExoAdminRoleId)') or (RoleDefinitionId eq '$($GlobalAdminRoleId)')" `-ExpandProperty RoleDefinition, Principal, DirectoryScope -All# Filter out the Exchange administrators[array]$ExoRoleMembers = $ActiveAssignments | Where-Object {$_.RoleDefinitionId -eq $ExoAdminRoleId} | Select-Object RoleDefinitionId, Principal, MemberTypeIf (!($ExoRoleMembers)) { Write-Output "Can't find any Exchange administrators! Exiting..." ; break }# Do the same for global administrators[array]$GARoleMembers = $ActiveAssignments | Where-Object {$_.RoleDefinitionId -eq $GlobalAdminRoleId} | Select-Object RoleDefinitionId, Principal, MemberTypeIf (!($GARoleMembers)) { Write-Output "Can't find any global administrators! Exiting..." ; break }Write-Output "Processing assignment information retrieved from Privileged Identity Management..."# Process Exchange administrators to extract accounts with individual assignments and those who receive assignments# through a groupForEach ($Member in $ExoRoleMembers) {$User = Get-MgUser -UserId $Member.Principal.Id -ErrorAction SilentlyContinue -Property Id, displayName, userPrincipalNameIf ($User) {$ReportLine = [PSCustomObject]@{User = $User.UserPrincipalNameUserId = $User.IdName = $User.DisplayNameRole = "Exchange administrator"MemberType = $Member.MemberType }$Report.Add($ReportLine)} Else { # Must be a group[array]$GroupMembers = Get-MgGroupMember -GroupId $Member.Principal.id -ErrorAction SilentlyContinueIf ($GroupMembers) {ForEach ($U in $GroupMembers) {$ReportLine = [PSCustomObject]@{User = $U.additionalProperties.userPrincipalNameUserId = $U.IdName = $U.additionalProperties.displayNameRole = "Exchange administrator"MemberType = $Member.MemberType }$Report.Add($ReportLine)}}} # End if} #End ForEach# Process global administratorsForEach ($Member in $GARoleMembers) {$User = Get-MgUser -UserId $Member.Principal.Id -ErrorAction SilentlyContinue -Property Id, displayName, userPrincipalNameIf ($User) {$ReportLine = [PSCustomObject]@{User = $User.UserPrincipalNameUserId = $User.IdName = $User.DisplayNameRole = "Global administrator"MemberType = $Member.MemberType }$Report.Add($ReportLine)} Else { # Must be a group[array]$GroupMembers = Get-MgGroupMember -GroupId $Member.Principal.Id -ErrorAction SilentlyContinueIf ($GroupMembers) {ForEach ($U in $GroupMembers) {$ReportLine = [PSCustomObject]@{User = $U.additionalProperties.userPrincipalNameUserId = $U.IdName = $U.additionalProperties.displayNameRole = "Global administrator"MemberType = $Member.MemberType }$Report.Add($ReportLine)}}} # End if} #End ForEach# Show what we found$Report | Out-GridView$Report | Export-CSV -NoTypeInformation $OutputFile# Create an array holding the user principal names of the two sets of administrator accounts[array]$AdminAccounts = $Report.User | Sort-Object -Unique# Find new Exchange user mailboxes that need to be processedWrite-Output "Checking Exchange Online user mailboxes to remove PowerShell access where needed..."[array]$ExoMailboxes = Get-ExoMailbox -Filter {CustomAttribute5 -eq $Null} -ResultSize Unlimited -RecipientTypeDetails UserMailbox -Properties CustomAttribute5ForEach ($Mbx in $ExoMailboxes) {# If not an admin holder, go ahead and block PowerShellIf ($Mbx.userPrincipalName -notin $AdminAccounts) {Write-Output ("Blocking PowerShell access for mailbox {0}..." -f $Mbx.displayName)Try {Set-User -Identity $Mbx.userPrincipalName -RemotePowerShellEnabled $False -Confirm:$False$MessageText = "PowerShell disabled on " + (Get-Date -format s)Set-Mailbox -Identity $Mbx.userPrincipalName -CustomAttribute5 $MessageText}Catch {Write-Output ("Error disabling PowerShell for mailbox {0}" -f $Mbx.userPrincipalNane )}}} # End ForEach# And make sure that mailboxes belonging to an admin who's received a recent assignment has PowerShell accessWrite-Output "Checking administrator mailboxes to make sure that they have PowerShell access..."ForEach ($Mbx in $AdminAccounts) {[string]$mbx = $mbx$PSEnabled = (Get-User -Identity $Mbx -ErrorAction SilentlyContinue).RemotePowerShellEnabledIf (!($PsEnabled)) {Write-Output ("Resetting PowerShell access for admin account {0}" -f $Mbx)Set-User -Identity $Mbx -RemotePowerShellEnabled $True -Confirm:$False}}Write-Host ("All done. CSV output is available in {0}." -f $OutputFile)
Attribution
Author
Office365itpros