MantraBlockSenders.psm1
|
# # MantraBlockSenders PowerShell Module # Simplified onboarding for Mantra Threats Block multi-tenant deployment # # Copyright (c) 2026 Mantra. All rights reserved. # $script:GuidPattern = '^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$' $script:BlockingRole = 'Tenant AllowBlockList Manager' function Test-MantraTenantMatch { param( [Parameter(Mandatory = $true)][string]$Organization, [string]$TenantId, [AllowEmptyCollection()][string[]]$AcceptedDomains = @() ) if ($Organization -match $script:GuidPattern) { return $TenantId -eq $Organization } return @($AcceptedDomains) -contains $Organization } function Test-MantraNotFoundError { param( [Parameter(Mandatory = $true)][System.Management.Automation.ErrorRecord]$ErrorRecord, [Parameter(Mandatory = $true)][string]$Identity ) if ($ErrorRecord.Exception -is [System.Management.Automation.CommandNotFoundException]) { return $false } if ($ErrorRecord.CategoryInfo.Category -eq [System.Management.Automation.ErrorCategory]::ObjectNotFound) { return $true } if ([string]$ErrorRecord.FullyQualifiedErrorId -like '*ManagementObjectNotFoundException*') { return $true } $escapedIdentity = [System.Management.Automation.WildcardPattern]::Escape($Identity) return $ErrorRecord.Exception.Message -like "*object '$escapedIdentity' couldn't be found*" } function Get-MantraServicePrincipalProblem { param( [Parameter(Mandatory = $true)]$ServicePrincipal, [Parameter(Mandatory = $true)][string]$AppId ) if ([string]$ServicePrincipal.AppId -eq $AppId) { return $null } return ("The Object ID belongs to another application (App ID $($ServicePrincipal.AppId)), " + "not to Mantra (App ID $AppId). Use the Object ID of the Mantra enterprise application " + "(Microsoft Entra admin center > Enterprise applications), then run the command again.") } function Get-MantraRoleGroupProblem { param( [Parameter(Mandatory = $true)]$RoleGroup ) $roles = @($RoleGroup.Roles | ForEach-Object { if ($_ -is [string]) { $_ } else { [string]$_.Name } }) if ($roles -contains $script:BlockingRole) { return $null } return ("The existing role group '$($RoleGroup.Name)' does not have the '$script:BlockingRole' role " + "(it has: $($roles -join ', ')). Add that role to the group, or delete the group, then run the command again.") } function Connect-MantraBlockSenders { <# .SYNOPSIS Configure Mantra Threats Block on your Microsoft 365 tenant .DESCRIPTION Automatically configures the Exchange Online permissions required for Mantra Threats Block (sender/domain blocking). This function: 1. Installs ExchangeOnlineManagement module if needed 2. Connects to Exchange Online (interactive login) and checks that it is the tenant given in -Organization 3. Checks any existing Service Principal and role group, and stops before changing anything if one of them is wrong 4. Creates the Service Principal and the limited RBAC role group (blocking only) when missing 5. Assigns the Service Principal to the role group .PARAMETER AppId Mantra application (client) ID This is the same for all clients and is provided on the authorization page .PARAMETER ObjectId Service Principal Object ID from your tenant Retrieved automatically during the authorization process .PARAMETER Organization Your tenant identifier. Accepts either: - Domain name: "client.onmicrosoft.com" (primary verified domain) - Tenant ID: "12345678-1234-1234-1234-123456789012" (tenant GUID) .EXAMPLE Connect-MantraBlockSenders ` -AppId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ` -ObjectId "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" ` -Organization "client.onmicrosoft.com" Configures Mantra Threats Block using domain name. .EXAMPLE Connect-MantraBlockSenders ` -AppId "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ` -ObjectId "bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb" ` -Organization "12345678-1234-1234-1234-123456789012" Configures Mantra Threats Block using tenant ID (GUID). .NOTES Requirements: - Global Administrator or Exchange Administrator role - Internet connection for initial setup - PowerShell 5.1 or later Estimated execution time: 30-60 seconds .LINK https://app.mantra.ms/docs #> [CmdletBinding()] param( [Parameter(Mandatory = $true, Position = 0)] [ValidatePattern('^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$')] [string]$AppId, [Parameter(Mandatory = $true, Position = 1)] [ValidatePattern('^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$')] [string]$ObjectId, [Parameter(Mandatory = $true, Position = 2)] [ValidatePattern('^([0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}|.*\.onmicrosoft\.com)$')] [string]$Organization ) $ErrorActionPreference = "Stop" Write-Host "" Write-Host "============================================" -ForegroundColor Cyan Write-Host " Mantra Threats Block - Configuration " -ForegroundColor Cyan Write-Host "============================================" -ForegroundColor Cyan Write-Host "" Write-Host "Organization : $Organization" -ForegroundColor Gray Write-Host "App ID : $AppId" -ForegroundColor Gray Write-Host "Object ID : $ObjectId" -ForegroundColor Gray Write-Host "" $AppName = "MantraBlockSendersBlock" $RoleName = "Mantra Blocage" try { # [1/5] Check/Install Exchange module Write-Host "[1/5] Checking Exchange module..." -ForegroundColor Yellow $module = Get-Module -ListAvailable -Name ExchangeOnlineManagement if (-not $module) { Write-Host " Installing ExchangeOnlineManagement..." -ForegroundColor Gray Install-Module -Name ExchangeOnlineManagement -Force -AllowClobber -Scope CurrentUser -ErrorAction Stop Write-Host " Installed [OK]" -ForegroundColor Green } else { Write-Host " OK (v$($module.Version)) [OK]" -ForegroundColor Green } Import-Module ExchangeOnlineManagement -ErrorAction Stop # [2/5] Connect to Exchange Online Write-Host "[2/5] Connecting to Exchange Online..." -ForegroundColor Yellow Write-Host " (Login window will open)" -ForegroundColor Gray Write-Host "" Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue Connect-ExchangeOnline -ShowBanner:$false -ErrorAction Stop $connection = Get-ConnectionInformation | Select-Object -Last 1 $acceptedDomains = @() if ($Organization -notmatch $script:GuidPattern) { $acceptedDomains = @(Get-AcceptedDomain | ForEach-Object { [string]$_.DomainName }) } if (-not (Test-MantraTenantMatch -Organization $Organization -TenantId ([string]$connection.TenantID) -AcceptedDomains $acceptedDomains)) { throw ("You are signed in to another tenant (tenant ID $($connection.TenantID)) than the one Mantra " + "expects ($Organization). Sign in with an administrator account of $Organization, then run the command again.") } Write-Host " Connected to $Organization [OK]" -ForegroundColor Green # [3/5] Check the existing configuration before changing anything Write-Host "[3/5] Checking existing configuration..." -ForegroundColor Yellow # A non-existent -Identity returns every object, so keep only the requested one. # Only a "not found" error means the object is missing; any other failure stops the setup. try { $existingSP = Get-ServicePrincipal -Identity $ObjectId -ErrorAction Stop | Where-Object { [string]$_.ObjectId -eq $ObjectId } | Select-Object -First 1 } catch { if (-not (Test-MantraNotFoundError -ErrorRecord $_ -Identity $ObjectId)) { throw } $existingSP = $null } if ($existingSP) { $spProblem = Get-MantraServicePrincipalProblem -ServicePrincipal $existingSP -AppId $AppId if ($spProblem) { throw $spProblem } } try { $existingRole = Get-RoleGroup -Identity $RoleName -ErrorAction Stop | Where-Object { [string]$_.Name -eq $RoleName } | Select-Object -First 1 } catch { if (-not (Test-MantraNotFoundError -ErrorRecord $_ -Identity $RoleName)) { throw } $existingRole = $null } if ($existingRole) { $roleProblem = Get-MantraRoleGroupProblem -RoleGroup $existingRole if ($roleProblem) { throw $roleProblem } } Write-Host " OK [OK]" -ForegroundColor Green # [4/5] Create the Service Principal and the role group Write-Host "[4/5] Configuring Service Principal and role group..." -ForegroundColor Yellow if ($existingSP) { Write-Host " Service Principal already exists [OK]" -ForegroundColor Green } else { New-ServicePrincipal -AppId $AppId -ServiceId $ObjectId -DisplayName $AppName -ErrorAction Stop | Out-Null Write-Host " Service Principal created [OK]" -ForegroundColor Green } if ($existingRole) { Write-Host " Role group already exists [OK]" -ForegroundColor Green } else { New-RoleGroup -Name $RoleName ` -Roles @($script:BlockingRole) ` -Description "Mantra Threats Block - Sender/domain blocking only" ` -ErrorAction Stop | Out-Null Write-Host " Role group created [OK]" -ForegroundColor Green } # [5/5] Assign role Write-Host "[5/5] Assigning permissions..." -ForegroundColor Yellow try { Add-RoleGroupMember -Identity $RoleName -Member $ObjectId -ErrorAction Stop Write-Host " Assigned [OK]" -ForegroundColor Green } catch { if ($_.Exception.Message -like "*already a member*") { Write-Host " Already member [OK]" -ForegroundColor Green } else { throw } } # Disconnect Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue # Success message Write-Host "" Write-Host "============================================" -ForegroundColor Green Write-Host " CONFIGURATION COMPLETED! " -ForegroundColor Green Write-Host "============================================" -ForegroundColor Green Write-Host "" Write-Host "Mantra Threats Block can now block malicious" -ForegroundColor Cyan Write-Host "senders and domains on your tenant." -ForegroundColor Cyan Write-Host "" Write-Host "You can close this window." -ForegroundColor Gray Write-Host "" } catch { Write-Host "" Write-Host "[X] Configuration failed" -ForegroundColor Red Write-Host " Error: $($_.Exception.Message)" -ForegroundColor Red Write-Host "" Write-Host "Troubleshooting:" -ForegroundColor Yellow Write-Host " - Verify you have Global Admin or Exchange Admin role" -ForegroundColor Gray Write-Host " - Check that all parameters are correct" -ForegroundColor Gray Write-Host " - Ensure ExchangeOnlineManagement module can be installed" -ForegroundColor Gray Write-Host "" Write-Host "Contact support: support@mantra.ms" -ForegroundColor Cyan Write-Host "" Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue throw } } function Test-MantraBlockSendersConnection { <# .SYNOPSIS Test Mantra Threats Block configuration .DESCRIPTION Verifies that Mantra Threats Block is correctly configured on your tenant by attempting to connect to Exchange Online and checking RBAC permissions. .EXAMPLE Test-MantraBlockSendersConnection .OUTPUTS System.Boolean Returns $true if configuration is valid, $false otherwise. #> [CmdletBinding()] param() Write-Host "" Write-Host "Testing Mantra Threats Block configuration..." -ForegroundColor Cyan Write-Host "" try { Connect-ExchangeOnline -ShowBanner:$false -ErrorAction Stop Write-Host " [OK] Exchange Online connection: OK" -ForegroundColor Green $testList = Get-TenantAllowBlockListItems -ListType Sender -ErrorAction Stop | Select-Object -First 1 Write-Host " [OK] Blocking permissions: OK" -ForegroundColor Green Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue Write-Host "" Write-Host "[OK] Configuration is valid!" -ForegroundColor Green Write-Host "" return $true } catch { Write-Host " [X] Configuration check failed" -ForegroundColor Red Write-Host "" Write-Host "[X] Configuration incomplete" -ForegroundColor Red Write-Host " Error: $($_.Exception.Message)" -ForegroundColor Red Write-Host "" Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue return $false } } function Get-MantraBlockSendersHelp { <# .SYNOPSIS Display help information .DESCRIPTION Shows available commands and support links #> Write-Host "" Write-Host "=================================================================" -ForegroundColor Cyan Write-Host "= Mantra Threats Block - Help =" -ForegroundColor Cyan Write-Host "=================================================================" -ForegroundColor Cyan Write-Host "" Write-Host "Available commands:" -ForegroundColor White Write-Host "" Write-Host " Connect-MantraBlockSenders" -ForegroundColor Yellow Write-Host " -AppId <guid> -ObjectId <guid> -Organization <tenant-guid|domain>" -ForegroundColor Gray Write-Host "" Write-Host " Test-MantraBlockSendersConnection" -ForegroundColor Yellow Write-Host " Verify configuration" -ForegroundColor Gray Write-Host "" Write-Host "Documentation: https://app.mantra.ms/docs" -ForegroundColor Cyan Write-Host "Support: support@mantra.ms" -ForegroundColor Cyan Write-Host "" } # Aliases New-Alias -Name "Setup-MantraBlockSenders" -Value "Connect-MantraBlockSenders" -ErrorAction SilentlyContinue New-Alias -Name "Test-MantraBlockSenders" -Value "Test-MantraBlockSendersConnection" -ErrorAction SilentlyContinue # Exports Export-ModuleMember -Function Connect-MantraBlockSenders, Test-MantraBlockSendersConnection, Get-MantraBlockSendersHelp Export-ModuleMember -Alias Setup-MantraBlockSenders, Test-MantraBlockSenders |