Get-AllowAndBlockListings.ps1

<#PSScriptInfo
 
.VERSION 2.0.2
 
.GUID 593703ef-d363-43ad-9f13-ea64af9ad05c
 
.AUTHOR Aaron Guilmette
 
.COMPANYNAME Microsoft
 
.COPYRIGHT 2021
 
.TAGS
 
.LICENSEURI
 
.PROJECTURI https://www.undocumented-features.com/2018/12/10/find-allowed-users-domains-and-ips-in-office-365/
 
.ICONURI
 
.EXTERNALMODULEDEPENDENCIES
 
.REQUIREDSCRIPTS
 
.EXTERNALSCRIPTDEPENDENCIES
 
.RELEASENOTES
 
.DESCRIPTION
Use this script to list allow entries across transport rules, content filter policies, and connection filter policies.
 
.PRIVATEDATA
 
#>


<#
.SYNOPSIS
Compile allow and block lists from Exchange Online Protection.
 
.PARAMETER Credential
Supply an Exchange Online credential.
 
.EXAMPLE
.\Get-AllowAndBlockListings.ps1
Generate listing of all allowed and block item entries.
 
.LINK
https://www.undocumented-features.com/2018/12/10/find-allowed-users-domains-and-ips-in-office-365/
 
.NOTES
2022-08-04 - Updated output path to default to desktop
2021-02-15 - Cleaned up filter syntax and formatting.
             Updated nomenclature in variables to "allow" and "block" instead of legacy biased terminology.
2021-02-13 - Updated and publisehd to PowerShell Gallery.
2018-12-10 - Original release.
 
All environments perform differently. Please test this code before using it
in production.
 
THIS CODE AND ANY ASSOCIATED INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK OF USE, INABILITY TO USE, OR RESULTS FROM THE USE OF
THIS CODE REMAINS WITH THE USER.
 
Author: Aaron Guilmette
        aaron.guilmette@microsoft.com
#>


param (
    [System.Management.Automation.PSCredential]$Credential,
    [string]$Output = [System.Environment]::GetFolderPath("Desktop") + "\" + (Get-Date -Format yyyy-MM-dd) + "_AllowedBlockedSenderOutput.csv"
)


function Write-Log([string[]]$Message, [string]$LogFile = $Script:LogFile, [switch]$ConsoleOutput, [ValidateSet("SUCCESS", "INFO", "WARN", "ERROR", "DEBUG")][string]$LogLevel)
{
    $Message = $Message + $Input
    If (!$LogLevel) { $LogLevel = "INFO" }
    switch ($LogLevel)
    {
        SUCCESS { $Color = "Green" }
        INFO { $Color = "White" }
        WARN { $Color = "Yellow" }
        ERROR { $Color = "Red" }
        DEBUG { $Color = "Gray" }
    }
    if ($Message -ne $null -and $Message.Length -gt 0)
    {
        $TimeStamp = [System.DateTime]::Now.ToString("yyyy-MM-dd HH:mm:ss")
        if ($LogFile -ne $null -and $LogFile -ne [System.String]::Empty)
        {
            Out-File -Append -FilePath $LogFile -InputObject "[$TimeStamp] [$LogLevel] :: $Message"
        }
        if ($ConsoleOutput -eq $true)
        {
            Write-Host "[$TimeStamp] [$LogLevel] :: $Message" -ForegroundColor $Color
        }
    }
}

# Detect Office 365 Session
Try
{
    $Sessions = Get-PSSession -ea stop | ? {$_.ComputerName -eq "outlook.office365.com" -and $_.State -eq "Opened" }
    If ($Sessions) { Write-Log -Message "Detected open Office 365 session." -Loglevel SUCCESS -ConsoleOutput }
    else
    {
        If (!($Credential))
        {
            $Credential = Get-Credential
        }
        $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $Credential -Authentication Basic -AllowRedirection
        Import-PSSession $Session
    }
}
Catch
{
    Write-Log -Message "No open Office 365 session detected." -LogLevel WARN
    If (!($Credential))
    {
        $Credential = Get-Credential
    }
    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $Credential -Authentication Basic -AllowRedirection
    Import-PSSession $Session
}

# Build Report Collection
Write-Log -Message "Initializing report." -LogLevel INFO -ConsoleOutput
[System.Collections.ArrayList]$report = @()

# Evaluate Content Filter Policies
Write-Log -Message "Collecting Content (Spam) Filter policies." -LogLevel INFO -ConsoleOutput
[array]$ContentFilterPolicies = Get-HostedContentFilterPolicy

Write-Log -Message "Retrieved $($ContentFilterPolicies.Count) content filter policies." -LogLevel INFO -ConsoleOutput
foreach ($policy in $ContentFilterPolicies)
{
    # Whitelisted Senders per policy
    foreach ($Sender in $policy.AllowedSenders)
    {
        $AllowedSenders = New-Object -TypeName PSObject
        $AllowedSenders | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "ContentOrSpamFilter"
        $AllowedSenders | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($policy.Name)
        $AllowedSenders | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($policy.Guid)
        $AllowedSenders | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "User"
        $AllowedSenders | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($Sender)
        $AllowedSenders | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Allow"
        $report += $AllowedSenders
        $AllowedSenders = $null
    }
    
    # Blocked Senders per policy
    foreach ($Sender in $policy.BlockedSenders)
    {
        $BlockedSenders = New-Object -TypeName PSObject
        $BlockedSenders | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "ContentOrSpamFilter"
        $BlockedSenders | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($policy.Name)
        $BlockedSenders | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($policy.Guid)
        $BlockedSenders | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "User"
        $BlockedSenders | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($Sender)
        $BlockedSenders | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Block"
        $report += $BlockedSenders
        $BlockedSenders = $null
    }
    
    # Allowed Domains per policy
    foreach ($Domain in $policy.AllowedSenderDomains)
    {
        $AllowedSenderDomains = New-Object -TypeName PSObject
        $AllowedSenderDomains | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "ContentOrSpamFilter"
        $AllowedSenderDomains | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($policy.Name)
        $AllowedSenderDomains | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($policy.Guid)
        $AllowedSenderDomains | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "Domain"
        $AllowedSenderDomains | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($Domain)
        $AllowedSenderDomains | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Allow"
        $report += $AllowedSenderDomains
        $AllowedSenderDomains = $null
    }
    
    # Blocked Domains per policy
    foreach ($Domain in $policy.BlockedSenderDomains)
    {
        $BlockedSenderDomains = New-Object -TypeName PSObject
        $BlockedSenderDomains | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "ContentOrSpamFilter"
        $BlockedSenderDomains | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($policy.Name)
        $BlockedSenderDomains | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($policy.Guid)
        $BlockedSenderDomains | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "Domain"
        $BlockedSenderDomains | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($Domain)
        $BlockedSenderDomains | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Block"
        $report += $BlockedSenderDomains
        $BlockedSenderDomains = $null
    }
}

# Evaluate Connection Filter Policies
Write-Log -Message "Collecting Connection Filter policies." -LogLevel INFO -ConsoleOutput

[array]$ConnectionFilterPolicies = Get-HostedConnectionFilterPolicy
Write-Log -Message "Retrieved $($ConnectionFilterPolicies.Count) connection filter policies." -LogLevel INFO -ConsoleOutput    

foreach ($policy in $ConnectionFilterPolicies)
{
    # IP allow list
    foreach ($IP in $policy.IPAllowList)
    {
        $IPAllow = New-Object -TypeName PSObject
        $IPAllow | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "ConnectionFilter"
        $IPAllow | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($policy.Name)
        $IPAllow | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($policy.Guid)
        $IPAllow | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "IP"
        $IPAllow | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($IP)
        $IPAllow | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Allow"
        $report += $IPAllow
        $IPAllow = $null
    }
    
    # IP Block list
    foreach ($IP in $policy.IPBlockList)
    {
        $IPBlock = New-Object -TypeName PSObject
        $IPBlock | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "ConnectionFilter"
        $IPBlock | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($policy.Name)
        $IPBlock | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($policy.Guid)
        $IPBlock | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "IP"
        $IPBlock | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($IP)
        $IPBlock | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Block"
        $report += $IPBlock
        $IPBlock = $null
    }
}

# Evaluate Transport Rules
Write-Log -Message "Collecting Transport Rules with an action of SetSCL -1 (bypass spam filtering)." -LogLevel INFO -ConsoleOutput
[array]$TransportRules = Get-TransportRule | ? { $_.SetScl -eq "-1" }
Write-Log -Message "Retrieved $($TransportRules.Count) matching transport rules." -LogLevel INFO -ConsoleOutput

foreach ($rule in $TransportRules)
{
    # Allowed Sender Domain (SenderDomainIs)
    foreach ($Domain in $rule.SenderDomainIs)
    {
        $DomainAllow = New-Object -TypeName PSObject
        $DomainAllow | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "TransportRule"
        $DomainAllow | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($rule.Name)
        $DomainAllow | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($rule.Guid)
        $DomainAllow | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "Domain"
        $DomainAllow | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($Domain)
        $DomainAllow | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Allow"
        $report += $DomainAllow
        $DomainAllow = $null
    }
    
    # Allowed Senders (From)
    foreach ($User in $rule.From)
    {
        $FromAllow = New-Object -TypeName PSObject
        $FromAllow | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "TransportRule"
        $FromAllow | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($rule.Name)
        $FromAllow | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($rule.Guid)
        $FromAllow | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "User"
        $FromAllow | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($User)
        $FromAllow | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Allow"
        $report += $FromAllow
        $FromAllow = $null
    }
    
    # Allowed IPs (SenderIpRanges)
    foreach ($IP in $rule.SenderIpRanges)
    {
        $IPAllow = New-Object -TypeName PSObject
        $IPAllow | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "TransportRule"
        $IPAllow | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($rule.Name)
        $IPAllow | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($rule.Guid)
        $IPAllow | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "IP"
        $IPAllow | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $($IP)
        $IPAllow | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Allow"
        $report += $IPAllow
        $IPAllow = $null
    }
    
    # Allowed Sender or Domain Contains (FromAddressContainsWords)
    foreach ($Sender in $rule.FromAddressContainsWords)
    {
        $SenderOrDomainContains = New-Object -TypeName PSObject
        $SenderOrDomainContains | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "TransportRule"
        $SenderOrDomainContains | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($rule.Name)
        $SenderOrDomainContains | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($rule.Guid)
        $SenderOrDomainContains | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "ContainsWords"
        $SenderOrDomainContains | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $Sender
        $report += $SenderOrDomainContains
        $SenderOrDomainContains = $null
    }
    
    # Allowed Sender or Domain Regex (FromAddressMatchesPatterns)
    foreach ($Sender in $rule.FromAddressMatchesPatterns)
    {
        $SenderOrDomainMatches = New-Object -TypeName PSObject
        $SenderOrDomainMatches | Add-Member -MemberType NoteProperty -Name "PolicyType" -Value "TransportRule"
        $SenderOrDomainMatches | Add-Member -MemberType NoteProperty -Name "PolicyName" -Value $($rule.Name)
        $SenderOrDomainMatches | Add-Member -MemberType NoteProperty -Name "PolicyGuid" -Value $($rule.Guid)
        $SenderOrDomainMatches | Add-Member -MemberType NoteProperty -Name "ObjectType" -Value "PatternMatch"
        $SenderOrDomainMatches | Add-Member -MemberType NoteProperty -Name "ObjectName" -Value $Sender
        $SenderOrDomainMatches | Add-Member -MemberType NoteProperty -Name "ActionType" -Value "Allow"
        $report += $SenderOrDomainMatches
        $SenderOrDomainMatches = $null
    }
}
$global:report = $report
$report | Export-Csv $Output -NoTypeInformation -Force
Write-Log -Message "Report $($Output) generated. You can also access the content by accessing the `$report object." -ConsoleOutput