Add Exchange Online PowerShell probe scripts

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ivo Oskamp 2026-05-06 13:51:24 +02:00
parent e304b2b3d4
commit 6741190342
3 changed files with 259 additions and 0 deletions

View File

@ -0,0 +1,135 @@
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$TenantId,
[Parameter(Mandatory=$true)][string]$ClientId,
[Parameter(Mandatory=$true)][string]$Organization,
[Parameter(Mandatory=$true)][string]$Mailbox,
[Parameter(Mandatory=$true)][string]$CertPath
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Write-JsonResult {
param($Payload)
Write-Output ($Payload | ConvertTo-Json -Depth 6 -Compress)
}
try {
Import-Module ExchangeOnlineManagement -ErrorAction Stop
} catch {
Write-JsonResult @{ ok = $false; error = "ExchangeOnlineManagement module not available: $($_.Exception.Message)" }
exit 0
}
try {
$pfxPwd = $env:CLEARVIEW_PFX_PASSWORD
if ([string]::IsNullOrEmpty($pfxPwd)) {
Write-JsonResult @{ ok = $false; error = "CLEARVIEW_PFX_PASSWORD not set in environment" }
exit 0
}
$securePwd = ConvertTo-SecureString -String $pfxPwd -AsPlainText -Force
Connect-ExchangeOnline `
-AppId $ClientId `
-Organization $Organization `
-CertificateFilePath $CertPath `
-CertificatePassword $securePwd `
-ShowBanner:$false `
-ShowProgress:$false `
-ErrorAction Stop | Out-Null
} catch {
Write-JsonResult @{ ok = $false; error = "Connect-ExchangeOnline failed: $($_.Exception.Message)" }
exit 0
}
$entries = New-Object System.Collections.Generic.List[object]
$warnings = New-Object System.Collections.Generic.List[string]
try {
$mb = Get-EXOMailbox -Identity $Mailbox -PropertySets All -ErrorAction Stop
# 1) Full Access (and other mailbox-level permissions)
try {
$perms = Get-EXOMailboxPermission -Identity $mb.UserPrincipalName -ErrorAction Stop |
Where-Object { $_.User -notlike 'NT AUTHORITY\SELF' -and $_.User -notlike 'S-1-5-*' -and -not $_.IsInherited -and $_.Deny -eq $false }
foreach ($p in $perms) {
$rights = @($p.AccessRights) -join ', '
$entries.Add([pscustomobject]@{
permission_type = 'FullAccess'
object_type = 'Mailbox'
object = $mb.UserPrincipalName
principal = [string]$p.User
role_name = $rights
})
}
} catch {
$warnings.Add("MailboxPermission: $($_.Exception.Message)")
}
# 2) Send As
try {
$sendAs = Get-EXORecipientPermission -Identity $mb.UserPrincipalName -ErrorAction Stop |
Where-Object { $_.Trustee -notlike 'NT AUTHORITY\SELF' -and $_.Trustee -notlike 'S-1-5-*' -and $_.AccessControlType -eq 'Allow' }
foreach ($p in $sendAs) {
$rights = @($p.AccessRights) -join ', '
$entries.Add([pscustomobject]@{
permission_type = 'SendAs'
object_type = 'Mailbox'
object = $mb.UserPrincipalName
principal = [string]$p.Trustee
role_name = $rights
})
}
} catch {
$warnings.Add("RecipientPermission: $($_.Exception.Message)")
}
# 3) Send on Behalf — from mailbox property
try {
if ($mb.GrantSendOnBehalfTo) {
foreach ($t in $mb.GrantSendOnBehalfTo) {
$entries.Add([pscustomobject]@{
permission_type = 'SendOnBehalf'
object_type = 'Mailbox'
object = $mb.UserPrincipalName
principal = [string]$t
role_name = 'SendOnBehalf'
})
}
}
} catch {
$warnings.Add("GrantSendOnBehalfTo: $($_.Exception.Message)")
}
# 4) Folder-level delegations on Calendar and Inbox
foreach ($folder in 'Calendar', 'Inbox') {
try {
$folderPath = "{0}:\{1}" -f $mb.UserPrincipalName, $folder
$fp = Get-EXOMailboxFolderPermission -Identity $folderPath -ErrorAction Stop |
Where-Object { $_.User.DisplayName -notin @('Default', 'Anonymous') -and $_.AccessRights -notcontains 'None' }
foreach ($p in $fp) {
$rights = @($p.AccessRights) -join ', '
$entries.Add([pscustomobject]@{
permission_type = "Folder:$folder"
object_type = 'MailboxFolder'
object = "$($mb.UserPrincipalName)/$folder"
principal = [string]$p.User.DisplayName
role_name = $rights
})
}
} catch {
$warnings.Add("FolderPermission ${folder}: $($_.Exception.Message)")
}
}
Write-JsonResult @{
ok = $true
mailbox = $mb.UserPrincipalName
entries = $entries
warnings = $warnings
}
} catch {
Write-JsonResult @{ ok = $false; error = $_.Exception.Message }
} finally {
try { Disconnect-ExchangeOnline -Confirm:$false -InformationAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null } catch {}
}

View File

@ -0,0 +1,67 @@
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$TenantId,
[Parameter(Mandatory=$true)][string]$ClientId,
[Parameter(Mandatory=$true)][string]$Organization,
[Parameter(Mandatory=$true)][string]$CertPath,
[Parameter(Mandatory=$false)][int]$MaxMailboxes = 50000
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Write-JsonResult {
param($Payload)
Write-Output ($Payload | ConvertTo-Json -Depth 4 -Compress)
}
try {
Import-Module ExchangeOnlineManagement -ErrorAction Stop
} catch {
Write-JsonResult @{ ok = $false; error = "ExchangeOnlineManagement module not available: $($_.Exception.Message)" }
exit 0
}
try {
$pfxPwd = $env:CLEARVIEW_PFX_PASSWORD
if ([string]::IsNullOrEmpty($pfxPwd)) {
Write-JsonResult @{ ok = $false; error = "CLEARVIEW_PFX_PASSWORD not set in environment" }
exit 0
}
$securePwd = ConvertTo-SecureString -String $pfxPwd -AsPlainText -Force
Connect-ExchangeOnline `
-AppId $ClientId `
-Organization $Organization `
-CertificateFilePath $CertPath `
-CertificatePassword $securePwd `
-ShowBanner:$false `
-ShowProgress:$false `
-ErrorAction Stop | Out-Null
} catch {
Write-JsonResult @{ ok = $false; error = "Connect-ExchangeOnline failed: $($_.Exception.Message)" }
exit 0
}
try {
$boxes = Get-EXOMailbox -ResultSize Unlimited -PropertySets Minimum -ErrorAction Stop |
Select-Object -ExpandProperty UserPrincipalName
if ($boxes.Count -gt $MaxMailboxes) {
Write-JsonResult @{
ok = $false
error = "Mailbox count $($boxes.Count) exceeds MaxMailboxes=$MaxMailboxes"
count = $boxes.Count
}
exit 0
}
Write-JsonResult @{
ok = $true
count = $boxes.Count
mailboxes = $boxes
}
} catch {
Write-JsonResult @{ ok = $false; error = $_.Exception.Message }
} finally {
try { Disconnect-ExchangeOnline -Confirm:$false -InformationAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null } catch {}
}

View File

@ -0,0 +1,57 @@
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)][string]$TenantId,
[Parameter(Mandatory=$true)][string]$ClientId,
[Parameter(Mandatory=$true)][string]$Organization,
[Parameter(Mandatory=$true)][string]$Mailbox,
[Parameter(Mandatory=$true)][string]$CertPath
)
$ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue'
function Write-Result {
param([bool]$Ok, [string]$Message)
$obj = [pscustomobject]@{ ok = $Ok; message = $Message }
Write-Output ($obj | ConvertTo-Json -Compress)
}
try {
Import-Module ExchangeOnlineManagement -ErrorAction Stop
} catch {
Write-Result -Ok $false -Message "ExchangeOnlineManagement module not available: $($_.Exception.Message)"
exit 0
}
try {
$pfxPwd = $env:CLEARVIEW_PFX_PASSWORD
if ([string]::IsNullOrEmpty($pfxPwd)) {
Write-Result -Ok $false -Message "CLEARVIEW_PFX_PASSWORD not set in environment"
exit 0
}
$securePwd = ConvertTo-SecureString -String $pfxPwd -AsPlainText -Force
Connect-ExchangeOnline `
-AppId $ClientId `
-Organization $Organization `
-CertificateFilePath $CertPath `
-CertificatePassword $securePwd `
-ShowBanner:$false `
-ShowProgress:$false `
-ErrorAction Stop | Out-Null
} catch {
Write-Result -Ok $false -Message "Connect-ExchangeOnline failed: $($_.Exception.Message)"
exit 0
}
try {
$box = Get-EXOMailbox -Identity $Mailbox -ErrorAction Stop -PropertySets Minimum
if ($null -eq $box) {
Write-Result -Ok $false -Message "Mailbox '$Mailbox' not found"
} else {
Write-Result -Ok $true -Message "OK"
}
} catch {
Write-Result -Ok $false -Message "Get-EXOMailbox failed: $($_.Exception.Message)"
} finally {
try { Disconnect-ExchangeOnline -Confirm:$false -InformationAction SilentlyContinue -ErrorAction SilentlyContinue | Out-Null } catch {}
}