Checks/Exchange/ARG.EXO.005.ps1
|
Register-ArgusCheck @{ Id = 'ARG.EXO.005' Title = 'User mailboxes of disabled accounts' Area = 'Exchange' Category = 'Lifecycle' Severity = 'Low' Description = 'User mailboxes (`RecipientTypeDetails` = UserMailbox) whose Microsoft Entra account is disabled (`accountEnabled` = false). Mailboxes on litigation hold or with in-place holds are counted as kept on purpose and not listed. Shared, room and equipment mailboxes are out of scope (their accounts are meant to be disabled).' Rationale = 'A disabled user with a live user mailbox is a leaver process that stopped halfway: mail keeps arriving, nobody reads it, and the mailbox usually keeps a licence. Converting it to a shared mailbox, delegating it or deleting it closes the process.' Remediation = @{ Steps = "1. Ask the manager whether incoming mail or content is still needed.`n2. Convert the mailbox to a shared mailbox and grant access to the manager, or set up forwarding for a limited period.`n3. Remove the licence and delete the account after the retention period; apply a hold first when legal requirements apply." PortalUrl = 'https://admin.exchange.microsoft.com/#/mailboxes' } References = @( @{ Title = 'Convert a user mailbox to a shared mailbox'; Url = 'https://learn.microsoft.com/en-us/microsoft-365/admin/email/convert-user-mailbox-to-shared-mailbox' } @{ Title = 'Remove a former employee from Microsoft 365'; Url = 'https://learn.microsoft.com/en-us/microsoft-365/admin/add-users/remove-former-employee' } ) Requires = @{ Services = @('Graph', 'Exchange') GraphScopes = @('User.Read.All') DataSets = @('Users', 'Mailboxes') } Tags = @('Exchange', 'Mailboxes', 'Lifecycle') Test = { param($Context) $users = @{} foreach ($user in @($Context.Data['Users'])) { $id = [string](Get-ArgusProperty $user 'id' '') if ($id) { $users[$id] = $user } } $mailboxes = @($Context.Data['Mailboxes'] | Where-Object { (Get-ArgusProperty $_ 'RecipientTypeDetails' '') -eq 'UserMailbox' }) $disabled = @($mailboxes | Where-Object { $user = $users[[string](Get-ArgusProperty $_ 'ExternalDirectoryObjectId' '')] $user -and (Get-ArgusProperty $user 'accountEnabled') -eq $false }) $held = @($disabled | Where-Object { (Get-ArgusProperty $_ 'LitigationHoldEnabled' $false) -eq $true -or @(Get-ArgusProperty $_ 'InPlaceHolds' @()).Count -gt 0 }) $findings = @($disabled | Where-Object { $_ -notin $held } | ForEach-Object { $user = $users[[string](Get-ArgusProperty $_ 'ExternalDirectoryObjectId' '')] [pscustomobject]@{ DisplayName = Get-ArgusProperty $_ 'DisplayName' '' UserPrincipalName = Get-ArgusProperty $_ 'UserPrincipalName' '' PrimarySmtpAddress = Get-ArgusProperty $_ 'PrimarySmtpAddress' '' Licensed = if (@(Get-ArgusProperty $user 'assignedLicenses' @()).Count -gt 0) { 'Yes' } else { 'No' } ArchiveStatus = Get-ArgusProperty $_ 'ArchiveStatus' '' ObjectId = Get-ArgusProperty $_ 'ExternalDirectoryObjectId' '' } } | Sort-Object UserPrincipalName) $note = if ($held.Count -gt 0) { " $($held.Count) more on hold are kept on purpose." } else { '' } $status = if ($findings.Count -gt 0) { 'Failed' } else { 'Passed' } New-ArgusResult -Status $status -Findings $findings -EvaluatedCount $mailboxes.Count ` -Summary ('{AffectedCount} of {EvaluatedCount} user mailboxes belong to disabled accounts and are not on hold.' + $note) } } |