{"id":6106,"date":"2025-12-24T11:17:08","date_gmt":"2025-12-24T09:17:08","guid":{"rendered":"https:\/\/www.msb365.blog\/?p=6106"},"modified":"2025-12-24T11:17:08","modified_gmt":"2025-12-24T09:17:08","slug":"mastering-exchange-online-security-automating-send-as-impersonation-audits","status":"publish","type":"post","link":"https:\/\/www.msb365.blog\/?p=6106","title":{"rendered":"Mastering Exchange Online Security: Automating Send-As &#038; Impersonation Audits"},"content":{"rendered":"<style>\n    \/* Scoped CSS for WordPress - Prevents conflicts with theme styles *\/\n    .exchange-audit-post {\n        --primary-color: #0078d4;\n        --secondary-color: #2b2b2b;\n        --accent-color: #00c7b7;\n        --bg-color: #f8f9fa;\n        --text-color: #333;\n        --code-bg: #1e1e1e;\n        --code-text: #d4d4d4;<\/p>\n<p>        font-family: 'Segoe UI', Inter, -apple-system, BlinkMacSystemFont, sans-serif;\n        line-height: 1.7;\n        color: var(--text-color);\n        background-color: var(--bg-color); \/* Creates the grey background area *\/\n        padding: 20px;\n        border-radius: 8px;\n    }<\/p>\n<p>    \/* Reset basic elements only within our container *\/\n    .exchange-audit-post * {\n        box-sizing: border-box;\n    }<\/p>\n<p>    \/* Container for the Blog Post Content *\/\n    .exchange-audit-post .blog-container {\n        max-width: 900px;\n        margin: 0 auto; \/* Centered *\/\n        background: white;\n        padding: 40px;\n        border-radius: 12px;\n        box-shadow: 0 10px 30px rgba(0,0,0,0.05);\n    }<\/p>\n<p>    \/* Headings *\/\n    .exchange-audit-post h1 {\n        font-size: 2.5rem;\n        color: var(--secondary-color);\n        margin-bottom: 10px;\n        margin-top: 0;\n        letter-spacing: -0.5px;\n        line-height: 1.2;\n    }<\/p>\n<p>    .exchange-audit-post h2 {\n        font-size: 1.8rem;\n        color: var(--primary-color);\n        margin-top: 40px;\n        border-bottom: 2px solid #eee;\n        padding-bottom: 10px;\n        line-height: 1.3;\n    }<\/p>\n<p>    .exchange-audit-post h3 {\n        font-size: 1.3rem;\n        color: var(--secondary-color);\n        margin-top: 30px;\n        font-weight: 600;\n    }<\/p>\n<p>    \/* Text Elements *\/\n    .exchange-audit-post p {\n        margin-bottom: 20px;\n        font-size: 1.1rem;\n    }<\/p>\n<p>    .exchange-audit-post ul {\n        margin-bottom: 20px;\n        padding-left: 20px;\n    }<\/p>\n<p>    .exchange-audit-post li {\n        margin-bottom: 10px;\n    }<\/p>\n<p>    \/* Code Blocks *\/\n    .exchange-audit-post pre {\n        background-color: var(--code-bg);\n        color: var(--code-text);\n        padding: 20px;\n        border-radius: 8px;\n        overflow-x: auto;\n        font-family: 'Consolas', 'Monaco', monospace;\n        font-size: 0.9rem;\n        border-left: 5px solid var(--accent-color);\n        margin: 20px 0;\n    }<\/p>\n<p>    .exchange-audit-post code {\n        font-family: 'Consolas', 'Monaco', monospace;\n    }<\/p>\n<p>    .exchange-audit-post p code {\n        background-color: #eef1f5;\n        padding: 2px 6px;\n        border-radius: 4px;\n        color: #c7254e;\n        font-size: 0.95em;\n    }<\/p>\n<p>    \/* Key Highlights Box *\/\n    .exchange-audit-post .highlight-box {\n        background-color: #e1f0fa;\n        border-left: 5px solid var(--primary-color);\n        padding: 20px;\n        margin: 30px 0;\n        border-radius: 4px;\n    }<\/p>\n<p>    \/* Button \/ Links *\/\n    .exchange-audit-post .download-btn {\n        display: inline-block;\n        background-color: var(--primary-color);\n        color: white;\n        padding: 12px 25px;\n        text-decoration: none;\n        border-radius: 50px;\n        font-weight: bold;\n        transition: background 0.3s ease;\n        margin-top: 20px;\n    }<\/p>\n<p>    .exchange-audit-post .download-btn:hover {\n        background-color: #005a9e;\n    }<\/p>\n<p>    .exchange-audit-post .meta-tags {\n        font-size: 0.9rem;\n        color: #777;\n        margin-bottom: 30px;\n        text-transform: uppercase;\n        letter-spacing: 1px;\n        font-weight: 600;\n    }<\/p>\n<p>    \/* Responsive tweaks *\/\n    @media (max-width: 768px) {\n        .exchange-audit-post .blog-container {\n            padding: 20px;\n        }\n        .exchange-audit-post h1 {\n            font-size: 2rem;\n        }\n    }<\/p>\n<\/style>\n<p><!-- Wrapper for WP isolation --><\/p>\n<div class=\"exchange-audit-post\">\n<article class=\"blog-container\">\n<div class=\"meta-tags\">PowerShell \u2022 Exchange Online \u2022 Security \u2022 Auditing<\/div>\n<p>As Exchange Online administrators, visibility is our most valuable asset. One of the most common security \u201cblind spots\u201d in growing organizations involves mailbox delegation. Specifically: <strong>Who has the right to send emails as someone else?<\/strong><\/p>\n<p>While the Microsoft 365 Admin Center allows you to check permissions one mailbox at a time, auditing an entire tenant for <em>Send As<\/em> permissions and global <em>ApplicationImpersonation<\/em> roles can be a nightmare. Today, I\u2019m sharing a PowerShell solution that automates this process and generates a beautiful, searchable HTML report.<\/p>\n<div class=\"highlight-box\">\n            <strong>Why is this important?<\/strong><br \/>\n            \u201cSend As\u201d allows a user (or hacker) to send an email that appears to come <em>directly<\/em> from the mailbox owner, with no \u201con behalf of\u201d tag. \u201cApplicationImpersonation\u201d is even more powerful, allowing applications to act as the user completely.\n        <\/div>\n<h2>The Solution: A Custom PowerShell Auditor<\/h2>\n<p>I wrote a PowerShell script that bridges the gap between raw data and actionable insights. Instead of staring at a CSV file, this script generates a modern, interactive HTML dashboard.<\/p>\n<h3>Key Features<\/h3>\n<ul>\n<li><strong>Full Tenant Scan:<\/strong> Iterates through all User and Shared Mailboxes.<\/li>\n<li><strong>Send-As Detection:<\/strong> Identifies explicit AD permissions (Active Directory Rights).<\/li>\n<li><strong>Impersonation Checks:<\/strong> Scans RBAC (Role Based Access Control) for the high-privilege <code>ApplicationImpersonation<\/code> role.<\/li>\n<li><strong>Visual Reporting:<\/strong> Produces a user-friendly HTML file with directional arrows indicating flow (Who \u2192 Can Access \u2192 Whom).<\/li>\n<li><strong>Searchable:<\/strong> Includes an embedded JavaScript search engine to filter results instantly.<\/li>\n<\/ul>\n<h2>How It Works<\/h2>\n<p>The script operates in three distinct phases:<\/p>\n<h3>1. Data Collection (Send As)<\/h3>\n<p>First, it connects to Exchange Online. It then loops through every mailbox using <code>Get-Mailbox<\/code>. For each mailbox, it runs <code>Get-RecipientPermission<\/code> to find any security identifier (SID) that has \u201cSendAs\u201d rights, filtering out the owner themselves (Self).<\/p>\n<h3>2. RBAC Analysis (Impersonation)<\/h3>\n<p>Unlike standard folder permissions, <em>ApplicationImpersonation<\/em> is an administrative role. The script uses <code>Get-ManagementRoleAssignment<\/code> to find service accounts or users who have been granted this role, and importantly, checks the <strong>Scope<\/strong> (whether it applies to the whole organization or just specific users).<\/p>\n<h3>3. Report Generation<\/h3>\n<p>Finally, it constructs an HTML file. It embeds CSS for styling and a small JavaScript snippet to handle the search bar logic, ensuring the report is a single, portable file you can share with auditors or management.<\/p>\n<h2>The Script<\/h2>\n<p>Here is the complete PowerShell code. Save this as <code>Audit-ExchangePermissions.ps1<\/code> and run it in a PowerShell terminal with Exchange Online Administrator rights.<\/p>\n<pre><code><#\r\n.SYNOPSIS\r\n    Generates an HTML Report for \"Send As\" and \"ApplicationImpersonation\" permissions.\r\n.DESCRIPTION\r\n    Scans all User and Shared mailboxes for 'SendAs' rights.\r\n    Checks RBAC assignments for 'ApplicationImpersonation'.\r\n    Outputs a searchable HTML file.\r\n#>\r\n\r\n# --- Configuration ---\r\n$ReportPath = \"$env:USERPROFILE\\Desktop\\Exchange_SendAs_Impersonation_Report.html\"\r\n$LogoUrl = \"https:\/\/img.icons8.com\/color\/48\/000000\/microsoft-exchange.png\"\r\n\r\n# --- Connection Check ---\r\ntry {\r\n    Get-Mailbox -ResultSize 1 -ErrorAction Stop -WarningAction SilentlyContinue | Out-Null\r\n    Write-Host \"Connection to Exchange Online verified.\" -ForegroundColor Green\r\n}\r\ncatch {\r\n    Write-Host \"Connecting to Exchange Online...\" -ForegroundColor Yellow\r\n    try {\r\n        Connect-ExchangeOnline -ErrorAction Stop\r\n    }\r\n    catch {\r\n        Write-Error \"Could not connect. Please check internet and permissions.\"\r\n        exit\r\n    }\r\n}\r\n\r\n# --- Data Collection ---\r\n$ReportData = @()\r\n\r\n# 1. Mailbox SendAs Checks\r\nWrite-Host \"Scanning Mailboxes for 'Send As' rights...\" -ForegroundColor Cyan\r\n$Mailboxes = Get-Mailbox -ResultSize Unlimited -RecipientTypeDetails UserMailbox,SharedMailbox\r\n\r\n$Counter = 0\r\n$Total = $Mailboxes.Count\r\n\r\nforeach ($Mailbox in $Mailboxes) {\r\n    $Counter++\r\n    Write-Progress -Activity \"Auditing Permissions\" -Status \"Processing: $($Mailbox.DisplayName)\" -PercentComplete (($Counter \/ $Total) * 100)\r\n    \r\n    # Filter 'SendAs' rights, excluding the user themselves (Self)\r\n    $Permissions = Get-RecipientPermission -Identity $Mailbox.Id -ResultSize Unlimited -Trustee $null | Where-Object { \r\n        ($_.AccessRights -like \"*SendAs*\") -and \r\n        ($_.Trustee -ne \"NT AUTHORITY\\SELF\") -and \r\n        ($_.Trustee -ne $Mailbox.UserPrincipalName)\r\n    }\r\n\r\n    foreach ($Perm in $Permissions) {\r\n        $ReportData += [PSCustomObject]@{\r\n            OwnerName    = $Mailbox.DisplayName\r\n            OwnerUPN     = $Mailbox.UserPrincipalName\r\n            Type         = if ($Mailbox.RecipientTypeDetails -eq \"UserMailbox\") { \"User Mailbox\" } else { \"Shared Mailbox\" }\r\n            Direction    = \"Sent By\"\r\n            Actor        = $Perm.Trustee\r\n            AccessRights = \"Send As\"\r\n        }\r\n    }\r\n}\r\n\r\n# 2. ApplicationImpersonation Checks (RBAC)\r\nWrite-Host \"Checking for 'ApplicationImpersonation' roles...\" -ForegroundColor Cyan\r\n$ImpersonationRoles = Get-ManagementRoleAssignment -Role \"ApplicationImpersonation\" -ResultSize Unlimited -ErrorAction SilentlyContinue\r\n\r\nforeach ($RoleAssignment in $ImpersonationRoles) {\r\n    $ScopeName = \"Entire Organization\"\r\n    if ($RoleAssignment.CustomRecipientWriteScope) {\r\n        $ScopeName = \"Scope: \" + $RoleAssignment.CustomRecipientWriteScope\r\n    } elseif ($RoleAssignment.RecipientReadScope -ne \"Organization\") {\r\n        $ScopeName = \"Scope: \" + $RoleAssignment.RecipientReadScope\r\n    }\r\n\r\n    $ReportData += [PSCustomObject]@{\r\n        OwnerName    = $ScopeName\r\n        OwnerUPN     = \"RBAC Scope\"\r\n        Type         = \"App Impersonation\" \r\n        Direction    = \"Impersonated By\"\r\n        Actor        = $RoleAssignment.RoleAssigneeName\r\n        AccessRights = \"ApplicationImpersonation\"\r\n    }\r\n}\r\n\r\nWrite-Progress -Activity \"Auditing Permissions\" -Completed\r\n\r\n# --- HTML Generation ---\r\nWrite-Host \"Generating HTML Report...\" -ForegroundColor Cyan\r\n\r\n$CSS = @\"\r\n<style>\r\n    body { font-family: 'Segoe UI', sans-serif; background-color: #f4f4f9; color: #333; padding: 20px; }\r\n    .container { max-width: 1200px; margin: 0 auto; background: white; padding: 30px; border-radius: 8px; box-shadow: 0 0 15px rgba(0,0,0,0.1); }\r\n    h1 { color: #0078d4; border-bottom: 2px solid #0078d4; padding-bottom: 10px; display: flex; align-items: center; }\r\n    .info-box { background-color: #e1f0fa; border-left: 5px solid #0078d4; padding: 15px; margin-bottom: 20px; }\r\n    #searchInput { width: 100%; padding: 12px; margin: 8px 0; border: 1px solid #ccc; border-radius: 4px; background-color: #f8f8f8; }\r\n    table { width: 100%; border-collapse: collapse; margin-top: 10px; font-size: 14px; }\r\n    th { background-color: #0078d4; color: white; text-align: left; padding: 12px 15px; }\r\n    td { padding: 12px 15px; border-bottom: 1px solid #ddd; }\r\n    tr:nth-child(even) { background-color: #f9f9f9; }\r\n    .badge { padding: 4px 8px; border-radius: 12px; font-size: 12px; font-weight: bold; color: white; }\r\n    .badge-shared { background-color: #ffc107; color: #333; }\r\n    .badge-user { background-color: #17a2b8; }\r\n    .badge-imper { background-color: #6f42c1; }\r\n    .arrow { color: #d9534f; font-weight: bold; }\r\n    .footer { margin-top: 30px; font-size: 12px; color: #777; text-align: center; }\r\n<\/style>\r\n\"@\r\n\r\n$ScriptJS = @\"\r\n<script>\r\n    function searchTable() {\r\n        var input = document.getElementById(\"searchInput\");\r\n        var filter = input.value.toUpperCase();\r\n        var table = document.getElementById(\"permissionTable\");\r\n        var tr = table.getElementsByTagName(\"tr\");\r\n        for (var i = 1; i < tr.length; i++) {\r\n            var td = tr[i].getElementsByTagName(\"td\");\r\n            var found = false;\r\n            for (var j = 0; j < td.length; j++) {\r\n                if (td[j] && (td[j].textContent || td[j].innerText).toUpperCase().indexOf(filter) > -1) {\r\n                    found = true; break;\r\n                }\r\n            }\r\n            tr[i].style.display = found ? \"\" : \"none\";\r\n        }\r\n    }\r\n<\/script>\r\n\"@\r\n\r\n$HTMLHeader = @\"\r\n<!DOCTYPE html>\r\n<html>\r\n<head><title>Exchange Permissions Report<\/title>$CSS $ScriptJS<\/head>\r\n<body>\r\n    <div class=\"container\">\r\n        <h1><img src=\"$LogoUrl\" width=\"40\" style=\"margin-right:15px\"> Exchange Permissions Audit<\/h1>\r\n        <div class=\"info-box\">\r\n            <strong>Audit Summary:<\/strong><br>\r\n            Found $($ReportData.Count) permission entries.\r\n        <\/div>\r\n        <input type=\"text\" id=\"searchInput\" onkeyup=\"searchTable()\" placeholder=\"Search users, emails, or permissions...\">\r\n        <table id=\"permissionTable\">\r\n            <thead>\r\n                <tr>\r\n                    <th>Type<\/th><th>Target (Owner\/Scope)<\/th><th>Access<\/th><th>Actor (Delegate)<\/th>\r\n                <\/tr>\r\n            <\/thead>\r\n            <tbody>\r\n\"@\r\n\r\n$HTMLRows = \"\"\r\nforeach ($Row in $ReportData) {\r\n    $BadgeClass = switch ($Row.Type) {\r\n        \"Shared Mailbox\" { \"badge-shared\" }\r\n        \"App Impersonation\" { \"badge-imper\" }\r\n        Default { \"badge-user\" }\r\n    }\r\n    \r\n    $HTMLRows += @\"\r\n                <tr>\r\n                    <td><span class=\"badge $BadgeClass\">$($Row.Type)<\/span><\/td>\r\n                    <td><strong>$($Row.OwnerName)<\/strong><br><span style=\"font-size:11px; color:#666;\">$($Row.OwnerUPN)<\/span><\/td>\r\n                    <td style=\"text-align:center;\"><span class=\"arrow\">\u2190 $($Row.AccessRights) \u2190<\/span><\/td>\r\n                    <td><strong>$($Row.Actor)<\/strong><\/td>\r\n                <\/tr>\r\n\"@\r\n}\r\n\r\n$HTMLFooter = @\"\r\n            <\/tbody>\r\n        <\/table>\r\n        <div class=\"footer\">Generated on $(Get-Date -Format \"yyyy-MM-dd\")<\/div>\r\n    <\/div>\r\n<\/body>\r\n<\/html>\r\n\"@\r\n\r\n$FinalHTML = $HTMLHeader + $HTMLRows + $HTMLFooter\r\n$FinalHTML | Out-File -FilePath $ReportPath -Encoding UTF8\r\nInvoke-Item $ReportPath\r\n<\/code><\/pre>\n<h2>Conclusion<\/h2>\n<p>Regular auditing of Exchange permissions is critical for maintaining a \u201cZero Trust\u201d environment. By automating the discovery of <em>Send As<\/em> and <em>Impersonation<\/em> rights, you ensure that no unauthorized account holds keys to your organization\u2019s communication channels.<\/p>\n<p>Feel free to customize the script to add more checks (like \u201cFull Access\u201d permissions) or change the branding to match your company colors!<\/p>\n<\/article>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>PowerShell \u2022 Exchange Online \u2022 Security \u2022 Auditing As Exchange Online administrators, visibility is our most valuable asset. One of the most common security \u201cblind spots\u201d in growing organizations involves mailbox delegation. Specifically: Who has the right to send emails as someone else? While the Microsoft 365 Admin Center allows you to check permissions one [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":6112,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_crdt_document":"","om_disable_all_campaigns":false,"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"footnotes":""},"categories":[1923,2,3],"tags":[],"class_list":["post-6106","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-microsoft-365","category-exchange","category-powershell"],"post_mailing_queue_ids":[],"_links":{"self":[{"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/posts\/6106","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=6106"}],"version-history":[{"count":3,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/posts\/6106\/revisions"}],"predecessor-version":[{"id":6111,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/posts\/6106\/revisions\/6111"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=\/wp\/v2\/media\/6112"}],"wp:attachment":[{"href":"https:\/\/www.msb365.blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=6106"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=6106"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.msb365.blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=6106"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}