Scripts/Get-Rules.ps1

$date = Get-Date -Format "yyyyMMddHHmm"
function Show-TransportRules
{
<#
    .SYNOPSIS
    Shows the transport rules in your organization.
 
    .DESCRIPTION
    Shows the transport rules in your organization.
     
    .Example
    Show-TransportRules
#>

    $transportRules = Get-TransportRule | Select-Object -Property Name,Description,CreatedBy,@{Name="WhenChanged";Expression={(Get-Date $_.WhenChanged).ToUniversalTime()}},State
    
    if ($null -ne $transportRules) {
        write-LogFile -Message "[INFO] Checking all TransportRules"
        foreach ($rule in $transportRules) {
            write-LogFile -Message "[INFO] Found a TransportRule" -Color "Green"
            write-LogFile -Message "Rule Name $($rule.name)" -Color "Yellow"
            write-LogFile -Message "Rule CreatedBy: $($rule.CreatedBy)" -Color "Yellow"
            write-LogFile -Message "When Changed: $($rule.WhenChanged)" -Color "Yellow"
            write-LogFile -Message "Rule State: $($rule.State)" -Color "Yellow"
            write-LogFile -Message "Description: $($rule.Description)" -Color "Yellow"
        }
    }
}

function Get-TransportRules
{
<#
    .SYNOPSIS
    Collects all transport rules in your organization.
 
    .DESCRIPTION
    Collects all transport rules in your organization.
    The output will be written to a CSV file called "TransportRules.csv".
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Rules
 
    .PARAMETER Encoding
    Encoding is the parameter specifying the encoding of the CSV output file.
    Default: UTF8
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
     
    .Example
    Get-TransportRules
#>


    [CmdletBinding()]
    param (
        [string]$OutputDir,
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Init-Logging
    Init-OutputDir -Component "Rules" -FilePostfix "TransportRules" -CustomOutputDir $OutputDir
    Write-LogFile -Message "=== Starting Transport Rules Collection ===" -Color "Cyan" -Level Standard

    if ($isDebugEnabled) {
        Write-LogFile -Message "[DEBUG] Retrieving transport rules from Exchange Online..." -Level Debug
        $performance = Measure-Command {
            $transportRules = Get-TransportRule | Select-Object -Property Name,Description,CreatedBy,@{Name="WhenChanged";Expression={(Get-Date $_.WhenChanged).ToUniversalTime()}},State,Priority,Mode
        }
        Write-LogFile -Message "[DEBUG] Transport rule retrieval took $([math]::round($performance.TotalSeconds, 2)) seconds" -Level Debug
    } else {
        $transportRules = Get-TransportRule | Select-Object -Property Name,Description,CreatedBy,@{Name="WhenChanged";Expression={(Get-Date $_.WhenChanged).ToUniversalTime()}},State,Priority,Mode
    }

    if ($null -eq $transportRules) {
        Write-LogFile -Message "[INFO] No transport rules found" -Color "Yellow" -Level Minimal
        return
    }

    if ($transportRules -isnot [array]) {
        $transportRules = @($transportRules)
    }

    $enabledCount = 0
    $disabledCount = 0

    $transportRules | ForEach-Object {
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Processing rule: $($_.Name)" -Level Debug
            Write-LogFile -Message "[DEBUG] State: $($_.State)" -Level Debug
            Write-LogFile -Message "[DEBUG] Priority: $($_.Priority)" -Level Debug
            Write-LogFile -Message "[DEBUG] Mode: $($_.Mode)" -Level Debug
            Write-LogFile -Message "[DEBUG] Created By: $($_.CreatedBy)" -Level Debug
            Write-LogFile -Message "[DEBUG] When Changed: $($_.WhenChanged)" -Level Debug
        }
        if ($_.State -eq "Enabled") {
            $enabledCount++
        }
        elseif ($_.State -eq "Disabled") {
            $disabledCount++
        }
        else {
            Write-LogFile -Message "[DEBUG] Unknown state value: $($_.State) for rule: $($_.Name)" -Level Standard
        }
    }

    $transportRules | Export-Csv -Path $script:outputFile -NoTypeInformation -Encoding $Encoding

    $summary = [ordered]@{
        "Transport Rules" = [ordered]@{
            "Total Rules" = $transportRules.Count
            "Enabled Rules" = $enabledCount
            "Disabled Rules" = $disabledCount
        }
    }

    Write-Summary -Summary $summary -Title "Transport Rules Summary"
}

function Show-MailboxRules
{
<#
    .SYNOPSIS
    Shows the mailbox rules in your organization.
 
    .DESCRIPTION
    Shows the mailbox rules in your organization.
 
    .PARAMETER UserIds
    UserIds is the Identity parameter specifying the mailbox(es) to check.
    Accepts a single UPN or multiple comma-separated UPNs.
    If omitted, all mailboxes in the organization are checked.
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
 
    .EXAMPLE
    Show-MailboxRules
    .EXAMPLE
    Show-MailboxRules -UserIds "HR@invictus-ir.com,Test@Invictus-ir.com"
#>

    [CmdletBinding()]
    param(
        [string]$UserIds,
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Init-Logging

    $amountofRules = 0

    if ([string]::IsNullOrWhiteSpace($UserIds)) {
        Write-LogFile -Message "[INFO] Retrieving all mailboxes..." -Level Standard
        $mailboxList = Get-Mailbox -ResultSize Unlimited | Select-Object -ExpandProperty UserPrincipalName
    } else {
        $mailboxList = $UserIds -split ',' | ForEach-Object { $_.Trim() }
    }

    foreach ($identity in $mailboxList) {
        Write-LogFile -Message "[INFO] Checking $identity..." -Level Standard

        $inboxrule = Get-InboxRule -Mailbox $identity
        if ($inboxrule) {
            Write-LogFile -Message "[INFO] Found InboxRule(s) for: $identity" -Color "Green" -Level Standard
            foreach ($rule in $inboxrule) {
                $amountofRules++
                Write-LogFile -Message "Username: $identity" -Color "Yellow"
                Write-LogFile -Message "RuleName: $($rule.Name)" -Color "Yellow"
                Write-LogFile -Message "RuleEnabled: $($rule.Enabled)" -Color "Yellow"
                Write-LogFile -Message "CopyToFolder: $($rule.CopyToFolder)" -Color "Yellow"
                Write-LogFile -Message "MoveToFolder: $($rule.MoveToFolder)" -Color "Yellow"
                Write-LogFile -Message "RedirectTo: $($rule.RedirectTo)" -Color "Yellow"
                Write-LogFile -Message "ForwardTo: $($rule.ForwardTo)" -Color "Yellow"
                Write-LogFile -Message "ForwardAsAttachmentTo: $($rule.ForwardAsAttachmentTo)" -Color "Yellow"
                Write-LogFile -Message "SoftDeleteMessage: $($rule.SoftDeleteMessage)" -Color "Yellow"
                Write-LogFile -Message "Description: $($rule.Description)" -Color "Yellow"
            }
        }
    }

    if ($amountofRules -gt 0) {
        Write-LogFile -Message "[INFO] A total of $amountofRules Inbox Rules found" -Color "Green"
    } else {
        Write-LogFile -Message "[INFO] No Inbox Rules found!" -Color "Yellow"
    }
}

function Get-MailboxRules
{
<#
    .SYNOPSIS
    Collects all the mailbox rules in your organization.
 
    .DESCRIPTION
    Collects all the mailbox rules in your organization.
    The output will be written to a CSV file called "InboxRules.csv".
     
    .Parameter UserIds
    UserIds is the Identity parameter specifies the Inbox rule that you want to view.
 
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Rules
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None: No logging
    Minimal: Critical errors only
    Standard: Normal operational logging
    Debug: Verbose logging for debugging purposes
    Default: Standard
 
    .PARAMETER Encoding
    Encoding is the parameter specifying the encoding of the CSV output file.
    Default: UTF8
     
    .Example
    Get-MailboxRules -UserIds Test@Invictus-ir.com
    Get-MailboxRules -UserIds "HR@invictus-ir.com,Test@Invictus-ir.com"
#>

    [CmdletBinding()]
    param(
        [string[]]$UserIds,
        [string]$OutputDir,
        [string]$Encoding = "UTF8",
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Init-Logging
    Init-OutputDir -Component "Rules" -FilePostfix "MailboxRules" -CustomOutputDir $OutputDir
    Write-LogFile -Message "=== Starting Mailbox Rules Collection ===" -Color "Cyan" -Level Standard

    $results = [System.Collections.Generic.List[object]]::new()
    $summary = @{
        TotalUsers = 0
        UsersWithRules = 0
        TotalRules = 0
        EnabledRules = 0
        ForwardingRules = 0
        ForwardAsAttachmentRules = 0
        RedirectRules = 0
        SoftDeleteRules = 0
        DeleteRules = 0
        HasAttachmentRules = 0
        StopProcessingRules = 0
        HighImportanceRules = 0
    }
    
    if ($null -eq $UserIds -or $UserIds.Count -eq 0 -or [string]::IsNullOrWhiteSpace($UserIds -join '')) {    
        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Processing scenario: All mailboxes" -Level Debug
            $performance = Measure-Command {
                $mailboxes = Get-Mailbox -ResultSize Unlimited
            }
            Write-LogFile -Message "[DEBUG] Get-Mailbox took $([math]::round($performance.TotalSeconds, 2)) seconds" -Level Debug
            Write-LogFile -Message "[DEBUG] Retrieved $($mailboxes.Count) mailboxes" -Level Debug
        } else {
            $mailboxes = Get-Mailbox -ResultSize Unlimited
        }
        $summary.TotalUsers = $mailboxes.Count

        foreach ($mailbox in $mailboxes) {
            Write-LogFile -Message "[INFO] Checking rules for: $($mailbox.UserPrincipalName)" -Level Standard
            $rules = Get-InboxRule -Mailbox $mailbox.UserPrincipalName
            
            if ($rules) {
                $summary.UsersWithRules++
                foreach ($rule in $rules) {
                    if ($isDebugEnabled) {
                        Write-LogFile -Message "[DEBUG] Processing rule: $($rule.Name)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Enabled: $($rule.Enabled)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Priority: $($rule.Priority)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Forward To: $($rule.ForwardTo)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Redirect To: $($rule.RedirectTo)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Delete Message: $($rule.DeleteMessage)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Soft Delete: $($rule.SoftDeleteMessage)" -Level Debug
                    }
                    $summary.TotalRules++
                    if ($rule.Enabled) { $summary.EnabledRules++ }
                    if ($rule.ForwardTo) { $summary.ForwardingRules++ }
                    if ($rule.ForwardAsAttachmentTo) { $summary.ForwardAsAttachmentRules++ }
                    if ($rule.RedirectTo) { $summary.RedirectRules++ }
                    if ($rule.SoftDeleteMessage) { $summary.SoftDeleteRules++ }
                    if ($rule.DeleteMessage) { $summary.DeleteRules++ }
                    if ($rule.HasAttachment) { $summary.HasAttachmentRules++ }
                    if ($rule.StopProcessingRules) { $summary.StopProcessingRules++ }
                    if ($rule.MarkImportance -eq "High") { $summary.HighImportanceRules++ }

                    $results.Add([PSCustomObject]@{
                        UserName = $mailbox.UserPrincipalName
                        RuleName = $rule.Name
                        Enabled = $rule.Enabled
                        Priority = $rule.Priority
                        RuleIdentity = $rule.RuleIdentity
                        StopProcessingRules = $rule.StopProcessingRules
                        CopyToFolder = ($rule.CopyToFolder -join ", ")
                        MoveToFolder = ($rule.MoveToFolder -join ", ")
                        RedirectTo = ($rule.RedirectTo -join ", ")
                        ForwardTo  = ($rule.ForwardTo -join ", ")
                        ForwardAsAttachmentTo = ($rule.ForwardAsAttachmentTo -join ", ")
                        ApplyCategory = ($rule.ApplyCategory -join ", ")
                        MarkImportance = $rule.MarkImportance
                        MarkAsRead = $rule.MarkAsRead
                        DeleteMessage = $rule.DeleteMessage
                        SoftDeleteMessage = $rule.SoftDeleteMessage
                        From = ($rule.From -join ", ")
                        SubjectContainsWords = ($rule.SubjectContainsWords -join ", ")
                        SubjectOrBodyContainsWords = ($rule.SubjectOrBodyContainsWords -join ", ")
                        BodyContainsWords = ($rule.BodyContainsWords -join ", ")
                        HasAttachment = $rule.HasAttachment
                        Description = $rule.Description
                        InError = $rule.InError
                        ErrorType = $rule.ErrorType
                    })
                }
            }
        }
    }
    else {
        $userList = $UserIds -split ','
        $summary.TotalUsers = $userList.Count

        if ($isDebugEnabled) {
            Write-LogFile -Message "[DEBUG] Processing scenario: Specific users" -Level Debug
            Write-LogFile -Message "[DEBUG] Users to process: $($userList -join ', ')" -Level Debug
            Write-LogFile -Message "[DEBUG] User count: $($userList.Count)" -Level Debug
        }

        foreach ($user in $userList) {
            $trimmedUser = $user.Trim()
            Write-LogFile -Message "[INFO] Checking rules for: $user" -Level Standard

            if ($isDebugEnabled) {
                Write-LogFile -Message "[DEBUG] Processing user: $trimmedUser" -Level Debug
                $rulePerformance = Measure-Command {
                    $rules = Get-InboxRule -Mailbox $trimmedUser
                }
                Write-LogFile -Message "[DEBUG] Get-InboxRule took $([math]::round($rulePerformance.TotalSeconds, 2)) seconds" -Level Debug
            } else {
                $rules = Get-InboxRule -Mailbox $trimmedUser
            }
            
            if ($rules) {
                $summary.UsersWithRules++
                if ($isDebugEnabled) {
                    Write-LogFile -Message "[DEBUG] Found $($rules.Count) rules for user: $trimmedUser" -Level Debug
                }
                foreach ($rule in $rules) {
                    if ($isDebugEnabled) {
                        Write-LogFile -Message "[DEBUG] Processing rule: $($rule.Name)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Enabled: $($rule.Enabled)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Priority: $($rule.Priority)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Forward To: $($rule.ForwardTo)" -Level Debug
                        Write-LogFile -Message "[DEBUG] Redirect To: $($rule.RedirectTo)" -Level Debug
                    }
                    $summary.TotalRules++
                    if ($rule.Enabled) { $summary.EnabledRules++ }
                    if ($rule.ForwardTo) { $summary.ForwardingRules++ }
                    if ($rule.ForwardAsAttachmentTo) { $summary.ForwardAsAttachmentRules++ }
                    if ($rule.RedirectTo) { $summary.RedirectRules++ }
                    if ($rule.SoftDeleteMessage) { $summary.SoftDeleteRules++}
                    if ($rule.DeleteMessage) { $summary.DeleteRules++ }
                    if ($rule.HasAttachment) { $summary.HasAttachmentRules++ }
                    if ($rule.StopProcessingRules) { $summary.StopProcessingRules++ }
                    if ($rule.MarkImportance -eq "High") { $summary.HighImportanceRules++ }  

                    $results.Add([PSCustomObject]@{
                        UserName = $user
                        RuleName = $rule.Name
                        Enabled = $rule.Enabled
                        Priority = $rule.Priority
                        RuleIdentity = $rule.RuleIdentity
                        StopProcessingRules = $rule.StopProcessingRules
                        CopyToFolder = ($rule.CopyToFolder -join ", ")
                        MoveToFolder = ($rule.MoveToFolder -join ", ")
                        RedirectTo = ($rule.RedirectTo -join ", ")
                        ForwardTo = ($rule.ForwardTo -join ", ")
                        ForwardAsAttachmentTo = ($rule.ForwardAsAttachmentTo -join ", ")
                        ApplyCategory = ($rule.ApplyCategory -join ", ")
                        MarkImportance = $rule.MarkImportance
                        MarkAsRead = $rule.MarkAsRead
                        DeleteMessage = $rule.DeleteMessage
                        SoftDeleteMessage = $rule.SoftDeleteMessage
                        From = ($rule.From -join ", ")
                        SubjectContainsWords = ($rule.SubjectContainsWords -join ", ")
                        SubjectOrBodyContainsWords = ($rule.SubjectOrBodyContainsWords -join ", ")
                        BodyContainsWords = ($rule.BodyContainsWords -join ", ")
                        HasAttachment = $rule.HasAttachment
                        Description = $rule.Description
                        InError = $rule.InError
                        ErrorType = $rule.ErrorType
                    })
                }
            }
        }
    }

    if ($results.Count -gt 0) {
        $results | Export-Csv -Path $script:outputFile -NoTypeInformation -Encoding $Encoding
    }

    $summaryOutput = [ordered]@{
        "User Statistics" = [ordered]@{
            "Users Processed" = $summary.TotalUsers
            "Users with Rules" = $summary.UsersWithRules
            "Total Rules Found" = $summary.TotalRules
            "Enabled Rules" = $summary.EnabledRules
        }
    }

    # Only add rule types that have counts > 0
    $ruleTypes = [ordered]@{}
    if ($summary.ForwardingRules -gt 0) { $ruleTypes["Forwarding Rules"] = $summary.ForwardingRules }
    if ($summary.ForwardAsAttachmentRules -gt 0) { $ruleTypes["Forward As Attachment Rules"] = $summary.ForwardAsAttachmentRules }
    if ($summary.RedirectRules -gt 0) { $ruleTypes["Redirect Rules"] = $summary.RedirectRules }
    if ($summary.SoftDeleteRules -gt 0) { $ruleTypes["Soft Delete Rules"] = $summary.SoftDeleteRules }
    if ($summary.DeleteRules -gt 0) { $ruleTypes["Delete Rules"] = $summary.DeleteRules }
    if ($summary.HasAttachmentRules -gt 0) { $ruleTypes["Has Attachment Rules"] = $summary.HasAttachmentRules }
    if ($summary.StopProcessingRules -gt 0) { $ruleTypes["Stop Processing Rules"] = $summary.StopProcessingRules }
    if ($summary.HighImportanceRules -gt 0) { $ruleTypes["High Importance Rules"] = $summary.HighImportanceRules }

    if ($ruleTypes.Count -gt 0) {
        $summaryOutput["Rule Types"] = $ruleTypes
    }

    Write-Summary -Summary $summaryOutput -Title "Mailbox Rules Summary"
}

function Get-MailboxRulesGraph
{
<#
    .SYNOPSIS
    Retrieves mailbox rules (Inbox Rules) for users using Microsoft Graph API.
 
    .DESCRIPTION
    Retrieves the mailbox rules for all users or specific users using the Graph API.
    The output identifies conditions (e.g., Subject contains) and actions (e.g., Forward to, Delete).
     
    .PARAMETER OutputDir
    OutputDir is the parameter specifying the output directory.
    Default: Output\Rules
 
    .PARAMETER Encoding
    Encoding is the parameter specifying the encoding of the CSV output file.
    Default: UTF8
 
    .PARAMETER LogLevel
    Specifies the level of logging:
    None, Minimal, Standard, Debug
    Default: Standard
 
    .PARAMETER UserIds
    UserId is the parameter specifying a single user ID or UPN to filter the results.
    Default: All users will be included if not specified.
     
    .EXAMPLE
    Get-MailboxRulesGraph
    Retrieves rules for all users.
 
    .EXAMPLE
    Get-MailboxRulesGraph -UserIds "HR@invictus-ir.com"
#>

    [CmdletBinding()]
    param(
        [string]$OutputDir,
        [string]$Encoding = "UTF8",
        [string]$UserIds,
        [ValidateSet('None', 'Minimal', 'Standard', 'Debug')]
        [string]$LogLevel = 'Standard'
    )

    Init-Logging
    Init-OutputDir -Component "Rules" -FilePostfix "MailboxRulesGraph" -CustomOutputDir $OutputDir

    $requiredScopes = @("User.Read.All", "MailboxSettings.Read")
    $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes
    
    Write-LogFile -Message "=== Starting Mailbox Rules Collection (Graph API) ===" -Color "Cyan" -Level Standard

    # Initialize Summary Trackers
    $summary = @{
        TotalUsers = 0
        UsersWithRules = 0
        TotalRules = 0
        EnabledRules = 0
        ForwardingRules = 0
        ForwardAsAttachmentRules = 0
        RedirectRules = 0
        DeleteRules = 0
        StopProcessingRules = 0
        Errors = 0
    }
    $results = [System.Collections.Generic.List[object]]::new()

    try {
        $usersToProcess = [System.Collections.Generic.List[object]]::new()

        if ($UserIds) {
            Write-LogFile -Message "[INFO] Filtering results for user: $UserIds" -Level Standard
            try {
                $userObj = Get-MgUser -UserId $UserIds -ErrorAction Stop
                $usersToProcess.Add($userObj)
            } catch {
                Write-LogFile -Message "[WARNING] User not found or error retrieving: $UserIds - $($_.Exception.Message)" -Color "Yellow" -Level debug
            }
        } else {
            Write-LogFile -Message "[INFO] Retrieving all users from tenant..." -Level Standard
            
            $allUsers = Get-MgUser -All -Property Id, UserPrincipalName, Mail, UserType
                $usersToProcess = $allUsers | Where-Object {
                    (-not [string]::IsNullOrWhiteSpace($_.Mail)) -and 
                    ($_.UserType -ne 'Guest') -and 
                    ($_.UserPrincipalName -notmatch '^HealthMailbox') -and
                    ($_.UserPrincipalName -notmatch '^DiscoverySearchMailbox')
                }

            Write-LogFile -Message "[INFO] Found $($usersToProcess.Count) enabled users" -Level Standard
        }
        
        $summary.TotalUsers = $usersToProcess.Count

        foreach ($user in $usersToProcess) {
            $upn = $user.UserPrincipalName
            Write-LogFile -Message "[INFO] Checking rules for: $upn" -Level Standard
            
            try {
                $rules = $null
                $retryCount = 0
                $maxRetries = 3
                $completed = $false

                while (-not $completed -and $retryCount -lt $maxRetries) {
                    try {
                        if ($isDebugEnabled) {
                            $perf = Measure-Command {
                                $rules = Get-MgUserMailFolderMessageRule -UserId $user.Id -MailFolderId "inbox" -ErrorAction Stop
                            }
                            Write-LogFile -Message "[DEBUG] API Call took $([math]::round($perf.TotalSeconds, 2))s" -Level Debug
                        } else {
                            $rules = Get-MgUserMailFolderMessageRule -UserId $user.Id -MailFolderId "inbox" -ErrorAction Stop
                        }
                        $completed = $true
                    }
                    catch {
                        $isThrottled = $false
                        $retryAfter  = $null

                        if ($_.Exception.Response -and $_.Exception.Response.StatusCode -eq 429) {
                            $isThrottled = $true
                            if ($_.Exception.Response.Headers -and $_.Exception.Response.Headers['Retry-After']) {
                                $retryAfter = [int]($_.Exception.Response.Headers['Retry-After'])
                            }
                        }
                        elseif ($_.Exception.Message -match 'TooManyRequests|throttl|429') {
                            $isThrottled = $true
                        }

                        if ($isThrottled) {
                            $retryCount++
                            $sleepTime = if ($retryAfter -and $retryAfter -gt 0) { $retryAfter } else { 5 * $retryCount }
                            Write-LogFile -Message "[WARNING] Throttled for user $upn. Retrying in $sleepTime seconds..." -Color "Yellow" -Level Standard
                            Start-Sleep -Seconds $sleepTime
                        } else {
                            throw $_
                        }
                    }
                }

                if ($rules) {
                    $summary.UsersWithRules++
                    $summary.TotalRules += @($rules).Count

                    if ($isDebugEnabled) {
                        Write-LogFile -Message "[DEBUG] Found $($rules.Count) rules" -Level Debug
                    }

                    foreach ($rule in $rules) {
                        if ($rule.IsEnabled) { $summary.EnabledRules++ }
                        if ($rule.Actions.ForwardTo) { $summary.ForwardingRules++ }
                        if ($rule.Actions.ForwardAsAttachmentTo) { $summary.ForwardAsAttachmentRules++ }
                        if ($rule.Actions.RedirectTo) { $summary.RedirectRules++ }
                        if ($rule.Actions.Delete) { $summary.DeleteRules++ }
                        if ($rule.Actions.StopProcessingRules) { $summary.StopProcessingRules++ }
                        
                        $forwardTo = if ($rule.Actions.ForwardTo) { ($rule.Actions.ForwardTo.EmailAddress.Address -join "; ") } else { $null }
                        $redirectTo = if ($rule.Actions.RedirectTo) { ($rule.Actions.RedirectTo.EmailAddress.Address -join "; ") } else { $null }
                        $forwardAsAttach = if ($rule.Actions.ForwardAsAttachmentTo) { ($rule.Actions.ForwardAsAttachmentTo.EmailAddress.Address -join "; ") } else { $null }
                        $moveTo = if ($rule.Actions.MoveToFolder) { "FolderID: $($rule.Actions.MoveToFolder)" } else { $null }
                        
                        $fromAddresses = if ($rule.Conditions.SenderContains) { ($rule.Conditions.SenderContains -join "; ") } else { $null }
                        $subjectContains = if ($rule.Conditions.SubjectContains) { ($rule.Conditions.SubjectContains -join "; ") } else { $null }
                        $bodyContains = if ($rule.Conditions.BodyContains) { ($rule.Conditions.BodyContains -join "; ") } else { $null }

                        $results.Add([PSCustomObject]@{
                            UserPrincipalName      = $upn
                            RuleName               = $rule.DisplayName
                            Sequence               = $rule.Sequence
                            Enabled                = $rule.IsEnabled
                            # Actions
                            ForwardTo              = $forwardTo
                            RedirectTo             = $redirectTo
                            ForwardAsAttachment    = $forwardAsAttach
                            Delete                 = $rule.Actions.Delete
                            PermanentDelete        = $rule.Actions.PermanentDelete
                            MoveToFolder           = $moveTo
                            StopProcessingRules    = $rule.Actions.StopProcessingRules
                            MarkAsRead             = $rule.Actions.MarkAsRead
                            # Conditions
                            From                   = $fromAddresses
                            SubjectContains        = $subjectContains
                            BodyContains           = $bodyContains
                            HasAttachments         = $rule.Conditions.HasAttachments
                            IsImportant            = $rule.Conditions.Importance
                            RuleId                 = $rule.Id
                        })
                    }
                }
            }
            catch {
                Write-LogFile -Message "[WARNING] Failed to retrieve rules for $upn`: $($_.Exception.Message)" -Color "Yellow" -Level Standard
                $summary.Errors++
            }
        }

        if ($results.Count -gt 0) {
            $results | Export-Csv -Path $script:outputFile -NoTypeInformation -Encoding $Encoding
        }

        $summaryOutput = [ordered]@{
            "Collection Statistics" = [ordered]@{
                "Total Users Scanned" = $summary.TotalUsers
                "Users with Rules"    = $summary.UsersWithRules
                "Total Rules Found"   = $summary.TotalRules
                "Errors"              = $summary.Errors
            }
            "Rule Types" = [ordered]@{
                "Enabled Rules"                  = $summary.EnabledRules
                "Forwarding Rules"               = $summary.ForwardingRules
                "Forward As Attachment Rules"    = $summary.ForwardAsAttachmentRules
                "Redirect Rules"                 = $summary.RedirectRules
                "Delete Rules"                   = $summary.DeleteRules
                "Stop Processing Rules"          = $summary.StopProcessingRules
            }
        }

        Write-Summary -Summary $summaryOutput -Title "Mailbox Rules (Graph) Summary"

    }
    catch {
        Write-LogFile -Message "[ERROR] Fatal error in Main Block: $($_.Exception.Message)" -Color "Red" -Level Minimal
        throw
    }
}