functions/User/Get-HawkUserHiddenRule.ps1

Function Get-HawkUserHiddenRule {
    <#
    .SYNOPSIS
    Pulls inbox rules for the specified user using EWS.
    .DESCRIPTION
    Pulls inbox rules for the specified user using EWS.
    Searches the resulting rules looking for "hidden" rules.
 
    Requires impersonation:
    https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-configure-impersonation
 
    Since the rules are hidden we have to pull it as a message instead of a rule.
    That means that the only information we can get back is the ID and Priority of the rule.
    Once a mailbox has been identified as having a hidden rule please use MFCMapi to review and remove the rule as needed.
 
    https://blogs.msdn.microsoft.com/hkong/2015/02/27/how-to-delete-corrupted-hidden-inbox-rules-from-a-mailbox-using-mfcmapi/
    .PARAMETER UserPrincipalName
    Single UPN of a user, comma separated list of UPNs, or array of objects that contain UPNs.
    .PARAMETER EWSCredential
    Credentials of a user that can impersonate the target user/users.
    Gather using (get-credential)
    Does NOT work with MFA protected accounts at this time.
    .OUTPUTS
 
    File: _Investigate.txt
    Path: \
    Description: Adds any hidden rules found here to be investigated
 
    File: EWS_Inbox_rule.csv
    Path: \<User>
    Description: Inbox rules that were found with EWS
    .EXAMPLE
 
    Get-HawkUserHiddenRule -UserPrincipalName user@contoso.com -EWSCredential (get-credential)
 
    Searches user@contoso.com looking for hidden inbox rules using the provided credentials
    .EXAMPLE
 
    Get-HawkUserHiddenRule -UserPrincipalName (get-mailbox -Filter {Customattribute1 -eq "C-level"})
 
    Looks for hidden inbox rules for all users who have "C-Level" set in CustomAttribute1
    #>

    param
    (
        [Parameter(Mandatory = $true)]
        [array]$UserPrincipalName,
        [System.Management.Automation.PSCredential]$EWSCredential

    )

    Test-EXOConnection
    Send-AIEvent -Event "CmdRun"

    # Verify our UPN input
    [array]$UserArray = Test-UserObject -ToTest $UserPrincipalName

    # Process thru each object recieved
    foreach ($Object in $UserArray) {

        # Push the UPN into $user for ease of use
        $user = $object.UserPrincipalName

        # Determine if the email address is null or empty
        # If it is write a warning and skip the rest of the script
        [string]$EmailAddress = (get-mailbox $user).primarysmtpaddress
        if ([string]::IsNullOrEmpty($EmailAddress)) {
            Write-Warning "No SMTP Address found Skipping"
            Return $null
        }

        # If we don't have a credential object then ask for creds and push them into the global scope
        if ($null -eq $EWSCredential) {
            Out-LogFile "Please provide credentials that have impersonation rights to the mailbox you are looking to check"
            $EWSCredential = Get-Credential
        }

        # Import the EWS Managed API
        if (Test-Path 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll') {
            Out-LogFile "Ews Managed API Found"
        }
        else {
            Write-Error "Please install EWS Managed API 2.2 `nhttp://www.microsoft.com/en-us/download/details.aspx?id=42951" -ErrorAction Stop
        }

        # Import the EWS managed API dll
        Import-Module 'C:\Program Files\Microsoft\Exchange\Web Services\2.2\Microsoft.Exchange.WebServices.dll'

        # Set up the EWS Connection
        Write-Host ("Setting up connection for " + $emailaddress) -ForegroundColor Green
        $exchService = New-Object Microsoft.Exchange.WebServices.Data.ExchangeService -ArgumentList ([Microsoft.Exchange.WebServices.Data.ExchangeVersion]::Exchange2013_Sp1)
        $exchService.Credentials = New-Object  Microsoft.Exchange.WebServices.Data.WebCredentials($EWSCredential.username, $EWSCredential.GetNetworkCredential().password);

        # If we have the global URL for EWS then we just use it since it should all be the same in this case
        # Otherwise we need to get it via autodiscover
        if ($null -eq $EWSUrl) {
            $exchService.AutodiscoverUrl($emailAddress, { $true })
            $exchService.url | Set-Variable -name EWSUrl -Scope Global
        }
        else {
            $exchService.url = $EWSUrl
        }

        # Set the connection up for impersonation so that we log into the mailbox we want not the one we have creds for
        $exchService.ImpersonatedUserId = New-Object Microsoft.Exchange.WebServices.Data.ImpersonatedUserId([Microsoft.Exchange.WebServices.Data.ConnectingIdType]::SmtpAddress, $emailAddress);

        # Add the Anchor mailbox to the http header
        $exchService.HttpHeaders.Add("X-AnchorMailbox", [string]$EmailAddress)

        # Using the exchService object connect and retrieve all inbox rules
        # This DID NOT work since it didn't pull back the hidden rule
        # $rules = $exchService.GetInboxRules($EmailAddress)

        # Bind to the inbox folder
        try {
            $inbox = [Microsoft.Exchange.WebServices.Data.Folder]::Bind($exchService, [Microsoft.Exchange.WebServices.Data.WellKnownFolderName]::Inbox)
        }
        catch {
            # If we don't have rights to impersonate throw in the log file and provide a better error
            if ($_.Exception.innerexception -like "*permission to impersonate*") {
                Out-LogFile ("[ERROR] - Account does not have Impersonation Rights on Mailbox: " + $EmailAddress)
                Out-LogFile "https://docs.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-configure-impersonation"
                Write-Error $_ -ErrorAction Stop
            }
            # If it isn't an impersonation error throw it and stop
            else {
                Write-Error $_ -ErrorAction Stop
            }
        }

        # Setup the search
        $SearchFilter = new-object Microsoft.Exchange.WebServices.Data.SearchFilter+IsEqualTo([Microsoft.Exchange.WebServices.Data.ItemSchema]::ItemClass, "IPM.Rule.Version2.Message")
        $Itemview = new-object Microsoft.Exchange.WebServices.Data.ItemView(500)
        $ItemView.Traversal = [Microsoft.Exchange.Webservices.Data.ItemTraversal]::Associated

        # Create our property set to view
        $PR_RULE_MSG_NAME = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x65EC, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
        $PR_RULE_MSG_PROVIDER = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x65EB, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::String)
        $PR_PRIORITY = New-Object Microsoft.Exchange.WebServices.Data.ExtendedPropertyDefinition(0x0026, [Microsoft.Exchange.WebServices.Data.MapiPropertyType]::Integer)
        $psPropset = new-object Microsoft.Exchange.WebServices.Data.PropertySet([Microsoft.Exchange.WebServices.Data.BasePropertySet]::IDOnly, $PR_RULE_MSG_NAME, $PR_RULE_MSG_PROVIDER, $PR_PRIORITY)

        # Add the property set to the item view
        $ItemView.PropertySet = $psPropset

        # Do the search and return the items
        $ruleResults = $inbox.finditems($SearchFilter, $Itemview)

        # Null our return arry and populate it
        [array]$ruleArray = $null
        $ruleResults | ForEach-Object { [array]$ruleArray += $_ }

        # Set our found flag to false
        $FoundHidden = $false

        # Check each rule
        Foreach ($rule in $ruleArray) {

            # If either Rule Name or Rule Provider are null then we need to flag it and return the priority of the rule
            if ([string]::IsNullOrEmpty($rule.ExtendedProperties[0].value) -or [string]::IsNullOrEmpty($rule.ExtendedProperties[1].value)) {
                $priority = ($rule.ExtendedProperties | Where-Object { $_.propertydefinition.tag -eq 38 }).value
                Out-LogFile ("Possible Hidden Rule found in mailbox: " + $EmailAddress + " -- Rule Priority: " + $priority) -notice
                $RuleOutput = $rule | Select-Object -Property ID, @{Name = "Priority"; Expression = { ($rule.ExtendedProperties | Where-Object { $_.propertydefinition -like "*38*" }).value } }
                $RuleOutput | Out-MultipleFileType -FilePrefix "EWS_Inbox_rule" -txt -user $user -append
                $FoundHidden = $true
            }

        }

        # If the flag wasn't set then we need to log that we didn't find any hidden rules for th euser
        if ($FoundHidden -eq $false) {
            Out-LogFile ("No Hidden rules found for mailbox: " + $EmailAddress)
        }

        # return $ruleArray
    }
}