diff --git a/Identify and Block External Email Forwarding (including Inbox Rules) in Exchange Online/BlockExternalEmailForwarding.ps1 b/Identify and Block External Email Forwarding (including Inbox Rules) in Exchange Online/BlockExternalEmailForwarding.ps1 new file mode 100644 index 0000000..7c95eb4 --- /dev/null +++ b/Identify and Block External Email Forwarding (including Inbox Rules) in Exchange Online/BlockExternalEmailForwarding.ps1 @@ -0,0 +1,493 @@ +<#------------------------------------------------------------------------------------------------------------------------------------------------------------- +Name: Identify and Block External Email Forwarding in Exchange Online Using PowerShell +Version: 1.0 +Website: o365reports.com + +~~~~~~~~~~~~~~~~~~ +Script Highlights: +~~~~~~~~~~~~~~~~~~ +1. The script automatically verifies and installs the Exchange PowerShell module (if not installed already) upon your confirmation. +2. Exports the ‘External email forwarding report’ and ‘Inbox rules with external forwarding report’ into a CSV file. +3. Blocks external forwarding configuration for all mailboxes upon confirmation. +4. Disables all the inbox rules with external forwarding configuration upon confirmation. +5. Allows to verify external email forwarding for specific mailboxes and blocks them. +6. Allows users to modify the generated CSV report and provide it as input later to block the respective external forwarding configuration. +7. Provides the detailed log file after removing the external forwarding configuration and disabling the inbox rules with external forwarding. +8. The script can be executed with an MFA-enabled account too. +9. The script supports Certificate-based authentication (CBA). + +For detailed script execution: https://o365reports.com/2024/07/23/identify-and-block-external-email-forwarding-in-exo-using-powershell/ +--------------------------------------------------------------------------------------------------------------------------------------------------------------#> + +param ( + [string] $CertificateThumbPrint, + [string] $ClientId, + [string] $Organization, + [string] $UserName, + [string] $Password, + [Switch] $ExcludeGuests, + [Switch] $ExcludeInternalGuests, + [String] $MailboxNames, + [String] $RemoveEmailForwardingFromCSV, + [String] $DisableInboxRuleFromCSV +) + +if($MailboxNames){ + if (-not (Test-Path $MailboxNames -PathType Leaf)) + { + Write-Host "Error: The specified CSV file does not exist or is not accessible." -ForegroundColor Red + Exit + } +} +Function WriteToLogFileEmail ($message) +{ + $message >> $global:EmailForwardingLogFile +} +Function WriteToLogFileInboxRule ($message) +{ + $message >> $global:InboxRuleLogFile +} +Function ConnectEXO{ + #check for EXO installation + $Module=Get-Module ExchangeOnlineManagement -ListAvailable + if($Module.count -eq 0) + { + Write-Host Exchange online powershell is not available -ForegroundColor Yellow + $Confirm = Read-Host Are you sure want to install module? [Y] Yes [N] No + if($Confirm -match "[yY]") + { + Write-Host Installing Exchange Online Powershell module + Install-Module -Name ExchangeOnlineManagement -Repository PSGallery -AllowClobber -Force -scope CurrentUser + Write-Host ExchangeOnlineManagement installed successfully... + } + else + { + Write-Host EXO module is required to connect Exchange Online.Please Install-module ExchangeOnlineManagement. + Exit + } + } + Write-Host "`nConnecting to Exchange Online..." + try{ + #connect to Exchange Online + if(($Organization -ne "") -and ($ClientId -ne "") -and ($CertificateThumbPrint -ne "")) + { + #Connect Exchange online using Certificate based Authentication + Connect-ExchangeOnline -CertificateThumbprint $CertificateThumbPrint -AppId $ClientId -Organization $Organization -ErrorAction stop -ShowBanner:$false + } + elseif(($UserName -ne "") -and ($Password -ne "")) + { + #Connect Exchange online using username and password + $SecuredPassword = ConvertTo-SecureString -AsPlainText $Password -Force + $Credential = New-Object System.Management.Automation.PSCredential $UserName, $SecuredPassword + Connect-ExchangeOnline -Credential $Credential -ErrorAction stop -ShowBanner:$false + } + else + { + Connect-ExchangeOnline -ErrorAction stop -ShowBanner:$false + } + } + catch + { + Write-Host "Error occurred: $($_.Exception.Message )" -ForegroundColor Red + Exit + } + Write-Host "`nExchangeOnline connected successfully" +} + +Function SplittingLegacyValue{ + param( + $value + ) + $SplittedValue = (($value).split(':') | select -Index 1) + $FinalValue = (($SplittedValue).split(']') | select -Index 0) + return $FinalValue +} + +Function SplittingQuotes{ + param( + $value + ) + $SplitValue = (($value).split('"') | Select -Index 1) + return $SplitValue +} + +Function FindForwardingActionIsExternal{ + param( + $Action + ) + if($Action.contains("[SMTP:")){ + $SplitQuoteValue = SplittingQuotes -value $Action + return $SplitQuoteValue + } + elseif($Action.contains("[EX:")){ + $SplittedLegacy = SplittingLegacyValue -value $Action + #To Check Internal Guests + if($ExcludeInternalGuests){ + if($global:InternalGuest.$SplittedLegacy){ + return + } + } + #To check Guest users + if($global:GuestUsers.$SplittedLegacy){ + if($ExcludeGuests){ + return + } + else{ + $SplitQuoteValue = SplittingQuotes -value $Action + return $SplitQuoteValue + } + } + #To check Mailusers + if($global:MailUsers.$SplittedLegacy){ + $SplitQuoteValue = SplittingQuotes -value $Action + return $SplitQuoteValue + } + #To check Mailcontacts + if($global:Contacts.$SplittedLegacy){ + $SplitQuoteValue = SplittingQuotes -value $Action + return $SplitQuoteValue + } + } +} + +Function GetInboxRule{ + param( + $Mailbox + ) + Write-Progress -Activity "Getting the inbox rule with external forwarding for the mailbox: $($Mailbox)" + Get-InboxRule -Mailbox $Mailbox | Where-Object {($_.ForwardAsAttachmentTo -ne $Empty -or $_.ForwardTo -ne $Empty -or $_.RedirectTo -ne $Empty) -and ($_.Enabled -eq $True) } | ForEach-Object{ + $ForwardTo = @() + $ForwardAsAttachmentTo = @() + $RedirectTo = @() + if($_.ForwardTo){ + ForEach($Forward in $_.ForwardTo){ + $IsExternal = FindForwardingActionIsExternal -Action $Forward + $ForwardTo = $ForwardTo + $IsExternal + } + } + if($_.RedirectTo){ + ForEach($Redirect in $_.RedirectTo){ + $IsExternal = FindForwardingActionIsExternal -Action $Redirect + $RedirectTo = $RedirectTo + $IsExternal + } + } + if($_.ForwardAsAttachmentTo){ + ForEach($ForwardAsAttach in $_.ForwardAsAttachmentTo){ + $IsExternal = FindForwardingActionIsExternal -Action $ForwardAsAttach + $ForwardAsAttachmentTo = $ForwardAsAttachmentTo + $IsExternal + } + } + if(($ForwardTo.count -gt 0) -or ($ForwardAsAttachmentTo.count -gt 0) -or ($RedirectTo.count -gt 0)){ + + $ExportResult = @{'Mailbox Name' =$_.MailboxOwnerId; 'User Principal Name' = $Mailbox; 'Inbox Rule Name' = $_.Name;'Rule Identity' = $_.Identity;'Forward To' = $ForwardTo -join(","); 'Forward As Attachment To' = $ForwardAsAttachmentTo -join(","); 'Redirect To' = $RedirectTo -join(",")} + $ExportResults = New-Object PSObject -Property $ExportResult + $ExportResults | Select-object 'Mailbox Name', 'User Principal Name', 'Inbox Rule Name', 'Rule Identity', 'Forward To', 'Forward As Attachment To', 'Redirect To' | Export-csv -path $global:ExportInboxRule -NoType -Append -Force + } + } + Write-Progress -Activity "Getting the inbox rule with external forwarding for the mailbox: $($Mailbox)" -Completed +} +Function CheckEmailForwardingForExternal{ + param( + $Mailbox + ) + Write-Progress -Activity "Getting External Forwarding for the Mailbox: $($Mailbox.DisplayName)" + $ForwardingAddress = '-' + $ForwardingSMTPAddress = '-' + $ExternalForwardingAddress = '-' + $ExternalForwardingSMTPAddress = '-' + $ExternalAddressFound = $True + $ExternalSMTPAddressFound = $True + #Check whether forwardingaddress is in mailcontact + if($Mailbox.ForwardingAddress){ + $ExternalAddressFound = $False + $ForwardingAddress = $Mailbox.ForwardingAddress + if($global:Contacts.$ForwardingAddress){ + $ExternalForwardingAddress = $ForwardingAddress + $ExternalAddressFound = $True + } + } + #check whether forwarding smtp address is external domain + if($Mailbox.ForwardingSMTPAddress){ + $ExternalSMTPAddressFound = $False + $ForwardingSMTPAddress = (($Mailbox.ForwardingSMTPAddress).split(":") | Select -Index 1) + $checkDomainIsInternal = (($Mailbox.ForwardingSMTPAddress).split("@") | Select -Index 1) + if(!$global:Domain.$checkDomainIsInternal) + { + $ExternalForwardingSMTPAddress = $ForwardingSMTPAddress + $ExternalSMTPAddressFound = $True + } + } + #To check Internal Guest Users + if($ExcludeInternalGuests){ + if(!$ExternalAddressFound){ + if($global:InternalGuest.$ForwardingAddress){ + $ExternalAddressFound = $True + } + } + if(!$ExternalSMTPAddressFound){ + if($global:InternalGuest.$ForwardingSMTPAddress){ + $ExternalSMTPAddressFound = $True + } + } + } + #To check Guest Users + if(!$ExternalAddressFound -or !$ExternalSMTPAddressFound){ + if(!$ExternalAddressFound){ + if($global:GuestUsers.$ForwardingAddress){ + if($ExcludeGuests){ + $ExternalAddressFound = $True + } + else{ + $ExternalForwardingAddress = $ForwardingAddress + $ExternalAddressFound = $True + } + } + } + if(!$ExternalSMTPAddressFound){ + if($global:GuestUsers.$ForwardingSMTPAddress){ + if($ExcludeGuests){ + $ExternalSMTPAddressFound = $True + } + else{ + $ExternalForwardingSMTPAddress = $ForwardingSMTPAddress + $ExternalSMTPAddressFound = $True + } + } + } + } + + #To Check MailUsers + if(!$ExternalAddressFound -or !$ExternalSMTPAddressFound){ + if(!$ExternalAddressFound){ + if($global:MailUsers.$ForwardingAddress){ + $ExternalForwardingAddress = $ForwardingAddress + $ExternalAddressFound = $True + } + } + if(!$ExternalSMTPAddressFound){ + if($global:MailUsers.$ForwardingSMTPAddress){ + $ExternalForwardingSMTPAddress = $ForwardingSMTPAddress + $ExternalSMTPAddressFound = $True + } + } + } + + if(($ExternalForwardingAddress -ne "-") -or ($ExternalForwardingSMTPAddress -ne "-")){ + $ExportResult = @{'Display Name' =$Mailbox.DisplayName; 'User Principal Name' = $Mailbox.UserPrincipalName; 'Forwarding Address' = $ExternalForwardingAddress; 'Forwarding SMTP Address' = $ExternalForwardingSMTPAddress; 'Deliver To Mailbox and Forward' = $Mailbox.DeliverToMailboxAndForward } + $ExportResults = New-Object PSObject -Property $ExportResult + $ExportResults | Select-object 'Display Name', 'User Principal Name', 'Forwarding Address', 'Forwarding SMTP Address', 'Deliver To Mailbox and Forward' | Export-csv -path $global:ExportEmailForwarding -NoType -Append -Force + } + Write-Progress -Activity "Getting External Forwarding for the Mailbox: $($Mailbox.DisplayName)" -Completed +} + +Function GetMailbox{ + if($MailboxNames){ + Import-Csv $MailboxNames | ForEach-object { + $Mailbox = Get-EXOMailbox $($_.'User Principal Name') -properties ForwardingAddress,ForwardingSMTPAddress,DeliverToMailboxAndForward | Where-Object {($_.ForwardingAddress -ne $null) -or ($_.ForwardingSMTPAddress -ne $null)} + if($Mailbox) { + CheckEmailForwardingForExternal -Mailbox $Mailbox + } + GetInboxRule -Mailbox $_.'User Principal Name' + } + } + else{ + Get-EXOMailbox -ResultSize unlimited -properties ForwardingAddress,ForwardingSMTPAddress,DeliverToMailboxAndForward | ForEach-Object { + if(($_.ForwardingAddress -ne $null) -or ($_.ForwardingSMTPAddress -ne $null)){ + CheckEmailForwardingForExternal -Mailbox $_ + } + GetInboxRule -Mailbox $_.UserPrincipalName + } + } +} + +Function RemoveEmailForwarding{ + param( + $CSV + ) + Write-Host "`nRemoving external forwarding..." + Import-Csv $CSV | ForEach-Object { + Write-Progress -Activity "Removing external forwarding for the mailbox: $($_.'Display Name')" + try { + if($_.'Forwarding Address' -ne '-'){ + if($_.'Forwarding SMTP Address' -ne '-'){ + Set-Mailbox $_.'User Principal Name' -ForwardingAddress $NULL -ForwardingSMTPAddress $NULL -ErrorAction Stop + WriteToLogFileEmail "External forwarding successfully removed from $($_.'Display Name') : ForwardingAddress - $($_.'Forwarding Address') , ForwardingSMTPAddress: $($_.'Forwarding SMTP Address')" + } + else{ + Set-Mailbox $_.'User Principal Name' -ForwardingAddress $Null -ErrorAction Stop + WriteToLogFileEmail "External forwarding successfully removed from $($_.'Display Name') : ForwardingAddress - $($_.'Forwarding Address')" + } + } + else{ + Set-Mailbox $_.'User Principal Name' -ForwardingSMTPAddress $NULL -ErrorAction Stop + WriteToLogFileEmail "External forwarding successfully removed from $($_.'Display Name') : ForwardingSMTPAddress - $($_.'Forwarding SMTP Address')" + } + } + catch { + Write-Host "Error occured while removing external forwarding configuration for $($_.'Display Name') : $($_.Exception.Message) ." -ForegroundColor Red + WriteToLogFileEmail "Error Occured: while removing external forwarding configuration for : $($_.'Display Name')" + } + } +} +Function DisableInboxRule{ + param( + $CSV + ) + Write-Host "`nDisabling inbox rule..." + Import-Csv $CSV | ForEach-Object{ + Write-Progress -Activity "Disabling inbox rule with external forwarding for the mailbox: $($_.'User Principal Name') " + try + { + Disable-InboxRule -Identity $_.'Rule Identity' -ErrorAction Stop -Confirm:$false + WriteToLogFileInboxRule "The Inbox rule with external forwarding : '$($_.'Inbox Rule Name')' present in : $($_.'Mailbox Name') mailbox is disabled." + } + catch + { + Write-Host "Error occured, while processing the inbox rule : $( $_.Exception.Message)" -ForegroundColor Red + WriteToLogFileInboxRule "Error Occured: While disabling inbox rule with external forwarding : $($_.'Inbox Rule Name') present in $($_.'Mailbox Name')" + } + } +} + +Function ConfirmationToBlock{ + $confirm = Read-Host "Enter your choice" + if($confirm -match "[yY]") + { + if((Test-Path -path $global:ExportEmailForwarding) -eq "True"){ + RemoveEmailForwarding -CSV $global:ExportEmailForwarding + } + if((Test-Path -Path $global:ExportInboxRule) -eq "True"){ + DisableInboxRule -CSV $global:ExportInboxRule + } + } +} + +Function InvokeOutputFiles{ + if (((Test-Path -Path $global:ExportEmailForwarding) -ne "True") -and ((Test-Path -Path $global:ExportInboxRule) -ne "True") ) { + Write-Host "`nExternal forwarding is not enabled for the given input." -ForegroundColor Green + Exit + } + else { + if (((Test-Path -Path $global:ExportEmailForwarding) -eq "True") -and ((Test-Path -Path $global:ExportInboxRule) -eq "True")) { + Write-Host "`nFollowing output files are generated and available in the directory $($global:Location):" -NoNewline -ForegroundColor Yellow + Write-Host "$global:ExportEmailForwarding , $global:ExportInboxRule" -ForegroundColor Cyan + $prompt = New-Object -ComObject wscript.shell + $userInput = $prompt.popup("Do you want to open output files?", 0, "Open Output File", 4) + if ($userInput -eq 6) { + Invoke-Item "$global:ExportEmailForwarding" + Invoke-Item "$global:ExportInboxRule" + } + Write-Host "`nDo you want to proceed with remove external forwarding and disable inbox rule with external forwarding? Yes[Y] No[N]" + ConfirmationToBlock + } + elseif((Test-Path -Path $global:ExportEmailForwarding) -eq "True") { + Write-Host "`nThe Output file available in the directory $($global:Location):" -NoNewline -ForegroundColor Yellow + Write-Host $global:ExportEmailForwarding -ForegroundColor Cyan + $prompt = New-Object -ComObject wscript.shell + $userInput = $prompt.popup("Do you want to open output file?", 0, "Open Output File", 4) + if ($userInput -eq 6) { + Invoke-Item "$global:ExportEmailForwarding" + } + Write-Host "`nDo you want to proceed with remove external forwarding? Yes[Y] No[N]" + ConfirmationToBlock + } + else { + Write-Host "`nThe output file available in the directory $($global:Location):" -NoNewline -ForegroundColor Yellow + Write-Host "$global:ExportInboxRule" -ForegroundColor Cyan + $prompt = New-Object -ComObject wscript.shell + $userInput = $prompt.popup("Do you want to open output file?", 0, "Open Output File", 4) + if ($userInput -eq 6) { + Invoke-Item "$global:ExportInboxRule" + } + Write-Host "`nDo you want to proceed with disable inbox rule with external forwarding? Yes[Y] No[N]" + ConfirmationToBlock + } + } +} + +#................................................Execution starts here..................................................... +#Calling Connection Function +ConnectEXO + +if(!$RemoveEmailForwardingFromCSV -and !$DisableInboxRuleFromCSV){ + Write-Host "`nFetching mailboxes whose emails are forwarded to external." + #Fetching domains + $global:Domain = @{} + Get-AcceptedDomain | ForEach-Object{ + $global:Domain[$_.DomainName] = $_.DomainName + } + #Fetching Guests + $global:GuestUsers = @{} + Get-User -resultsize unlimited | where-object {$_.UserType -eq 'Guest'} | select Identity,LegacyExchangeDN,UserPrincipalName | ForEach-Object{ + $global:GuestUsers[$_.Identity] = $_.Identity + $global:GuestUsers[$_.LegacyExchangeDN] = $_.LegacyExchangeDN + $global:GuestUsers[$_.UserPrincipalName] = $_.UserPrincipalName + } + #FetchingInternalGuests + if($ExcludeInternalGuests){ + $global:InternalGuest = @{} + Get-User -resultsize unlimited | Where-Object {$_.UserPersona -eq 'InternalGuest'} | Select Identity,LegacyExchangeDN,UserPrincipalName | ForEach-Object{ + $global:InternalGuest[$_.Identity] = $_.Identity + $global:InternalGuest[$_.LegacyExchangeDN] = $_.LegacyExchangeDN + $global:InternalGuest[$_.UserPrincipalName] = $_.UserPrincipalName + } + } + #Fetching contacts + $global:Contacts = @{} + Get-MailContact -resultsize unlimited | select Identity,LegacyExchangeDN | ForEach-Object{ + $global:Contacts[$_.Identity] = $_.Identity + $global:Contacts[$_.LegacyExchangeDN] = $_.LegacyExchangeDN + } + #Mailusers + $global:MailUsers = @{} + Get-MailUser -resultsize unlimited | select Identity,LegacyExchangeDN,UserPrincipalName | ForEach-Object{ + $global:MailUsers[$_.Identity] = $_.Identity + $global:MailUsers[$_.LegacyExchangeDN] = $_.LegacyExchangeDN + $global:MailUsers[$_.UserPrincipalName] = $_.UserPrincipalName + } + #OutputFiles + $global:ExportEmailForwarding = "ExternalEmailForwardingReport_" + ((Get-Date -format "MMM-dd hh-mm-ss tt").ToString()) + ".csv" + $global:ExportInboxRule = "InboxRulesWithExternalForwardingReport_" + ((Get-Date -format "MMM-dd hh-mm-ss tt").ToString()) + ".csv" +} +$global:EmailForwardingLogFile = "RemovedExternalForwarding_LogFile_" + ((Get-Date -format "MMM-dd hh-mm-ss tt").ToString()) + ".txt" +$global:InboxRuleLogFile = "DisableInboxRuleWithExternalForwarding_LogFile_" + ((Get-Date -format "MMM-dd hh-mm-ss tt").ToString()) + ".txt" +$global:Location = Get-Location + +if(!$RemoveEmailForwardingFromCSV -and !$DisableInboxRuleFromCSV){ + GetMailbox + InvokeOutputFiles +} + +if($RemoveEmailForwardingFromCSV){ + if((Test-Path -path $RemoveEmailForwardingFromCSV) -eq "True"){ + RemoveEmailForwarding -CSV $RemoveEmailForwardingFromCSV + } + else{ + Write-Host "`nError: The specified CSV file for removing external forwarding" -NoNewline -ForegroundColor Red + Write-Host " $RemoveEmailForwardingFromCSV " -Nonewline + Write-Host "is not valid. Please check the path." -ForegroundColor Red + } +} +if($DisableInboxRuleFromCSV){ + if((Test-Path -path $DisableInboxRuleFromCSV) -eq "True"){ + DisableInboxRule -CSV $DisableInboxRuleFromCSV + } + else{ + Write-Host "`nError: The specified CSV file for disabling inbox rule" -NoNewline -ForegroundColor Red + Write-Host " $DisableInboxRuleFromCSV " -Nonewline + Write-Host "is not valid. Please check the path." -ForegroundColor Red + } +} + +if (((Test-Path -Path $global:EmailForwardingLogFile) -eq "True")){ + Write-host "`nThe log file $global:EmailForwardingLogFile available in the directory $global:Location" -ForegroundColor yellow +} + + +if (((Test-Path -Path $global:InboxRuleLogFile) -eq "True")){ + Write-host "`nThe log file $global:InboxRuleLogFile available in the directory $global:Location" -ForegroundColor yellow +} + +Disconnect-ExchangeOnline -confirm:$false +Write-Host `n~~ Script prepared by AdminDroid Community ~~`n -ForegroundColor Green +Write-Host "~~ Check out " -NoNewline -ForegroundColor Green; Write-Host "admindroid.com" -ForegroundColor Yellow -NoNewline; Write-Host " to get access to 1800+ Microsoft 365 reports. ~~" -ForegroundColor Green `n`n \ No newline at end of file