> ## Documentation Index
> Fetch the complete documentation index at: https://support.configview.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Exchange Online setup

ConfigView shows where mail leaves the company without anyone noticing: **mailboxes that auto-forward** outside, **mail flow rules** that redirect, BCC or copy mail to outside addresses, whether your **outbound policy allows automatic forwarding** at all, and **who holds Full Access, Send As or send-on-behalf** on someone else's mailbox.

Microsoft has no supported API for these settings; only **Exchange Online PowerShell** can read them. So this integration works the other way round from the others: **a short script runs in your own Azure Automation account**, reads the settings with Microsoft's own PowerShell module under the account's **managed identity**, and sends the results to your ConfigView server. ConfigView never holds a credential for your Exchange.

You will end up with: one **Azure Automation account** running the script once a day, its managed identity holding a **read-only** role, and one **ConfigView API key** that can do nothing except upload these results.

> **What is read, and what is never read.** The script runs only `Get-*` cmdlets and sends: mail flow rules (name, state, mode, priority, the outside addresses they redirect/BCC/copy to, and the *names* of their conditions and actions), mailboxes that forward or have send-on-behalf delegates (address, display name, type, where they forward, whether a copy is kept, the delegates), mailbox **Full Access** and **Send As** grants that were added on purpose, your **outbound spam policies'** auto-forwarding setting and who they apply to, **remote domains** and **accepted domains**. It **never** reads mail, folders, calendars or contacts, never sends the words a rule matches on or rule comments, and never changes anything.

> **Inbox rules are covered separately.** Rules people create in Outlook (forward my mail to…) come from the [Microsoft 365](https://support.configview.com/microsoft-365.md) integration through Microsoft Graph, with no extra setup; the "Inbox rules forwarding mail outside the company" check reads them.

***

## What you need

* A Microsoft 365 tenant with **Exchange Online**, and an **Azure subscription** in the same tenant to hold the Automation account (its cost for one short daily job is typically zero to a few cents a month).
* A person who is a **Global Administrator** or **Privileged Role Administrator** (to grant the managed identity its two permissions in Step 3), and **Contributor** on the Azure subscription or resource group (Steps 2, 4 and 5).
* A ConfigView **admin** (Step 1).

***

## Step 1: Create a ConfigView API key for the upload

1. In ConfigView, go to **Admin > Integrations** and open **API keys** (or avatar menu → **API Keys**). See [REST API](https://support.configview.com/api.md) for more on keys.
2. **Create key**, name it `Exchange export runbook`.
3. Under the allowed scopes, select **all seven** `Push: Exchange Online …` entries (mail flow rules, mailbox forwarding + send on behalf, mailbox permissions, Send As grants, outbound spam policies, remote domains, accepted domains). Select nothing else.
4. Copy the key (`cv_live_…`). It is shown once.

Also open **Admin > Integrations → Exchange Online** and **select** it, so your server creates the tables the uploads land in.

***

## Step 2: Create the Automation account

1. In the [Azure portal](https://portal.azure.com), **Create a resource → Automation**.
2. Pick the subscription, a resource group and a region, and a name such as `configview-exchange-export`. On the **Advanced** tab leave **System assigned** managed identity **on** (the default).
3. Open the new account → **Runtime Environments** → **Create**: name `ps72-exo`, language **PowerShell**, runtime **7.2**. Under **Packages**, add from the gallery **ExchangeOnlineManagement** (version 3.x).

***

## Step 3: Give the managed identity read-only Exchange access

The identity needs two things: Microsoft's **Exchange.ManageAsApp** permission (lets an app sign in to Exchange Online PowerShell) and the **Global Reader** role (what it may read). Global Reader can read everything in the tenant and change nothing.

**3a. Exchange.ManageAsApp.** There is no portal button for managed identities; run this once in [Azure Cloud Shell](https://shell.azure.com) (PowerShell) or any PowerShell with the Microsoft Graph module, signed in as a Global Administrator or Privileged Role Administrator:

```powershell theme={null}
Connect-MgGraph -Scopes AppRoleAssignment.ReadWrite.All, Application.Read.All
$mi  = Get-MgServicePrincipal -Filter "displayName eq 'configview-exchange-export'"   # your Automation account name
$exo = Get-MgServicePrincipal -Filter "appId eq '00000002-0000-0ff1-ce00-000000000000'" # Office 365 Exchange Online
$role = $exo.AppRoles | Where-Object Value -eq 'Exchange.ManageAsApp'
New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $mi.Id -PrincipalId $mi.Id -ResourceId $exo.Id -AppRoleId $role.Id
```

**3b. Global Reader.** In the [Microsoft Entra admin center](https://entra.microsoft.com): **Roles & admins → Global Reader → Add assignments**, search for the Automation account's name (it is listed as an enterprise application / managed identity) and assign it.

Microsoft documents both steps in [App-only authentication for Exchange Online PowerShell with a managed identity](https://learn.microsoft.com/powershell/exchange/connect-exo-powershell-managed-identity).

***

## Step 4: Add three variables

In the Automation account → **Shared Resources → Variables → Add a variable**:

| Name | Type | Encrypted | Value |
| - | - | - | - |
| `ConfigViewUrl` | String | No | Your ConfigView address, e.g. `https://acme.configview.com` |
| `ConfigViewApiKey` | String | **Yes** | The key from Step 1 (`cv_live_…`) |
| `ExchangeOrganization` | String | No | Your initial domain, e.g. `acme.onmicrosoft.com` (Microsoft 365 admin center → Settings → Domains) |

Optional: `ConfigViewMaxMinutes` (default `150`), the time the script may spend before it stops. Full Access grants are read one mailbox at a time, so very large tenants (tens of thousands of mailboxes) may need longer; if the budget runs out the script sends everything else and skips that section rather than sending half of it.

***

## Step 5: Import the script and schedule it

1. Download the script from **your own ConfigView server** (it always matches your server's version):

   `https://{companyname}.configview.com/api/v1/ingest/exchange/runbook.ps1`

2. In the Automation account → **Process Automation → Runbooks → Import a runbook**: upload `Invoke-ConfigViewExchangeExport.ps1`, type **PowerShell**, runtime environment **ps72-exo** (Step 2). **Publish** it.

3. **Start** it once and open the job's **Output**: it lists each section with its item count and ends with `ConfigView export run … complete`.

4. **Link to schedule → Create a new schedule**: once a day, at a quiet hour.

***

## Step 6: Check it in ConfigView

Open **Admin > Integrations → Exchange Online**. The health check reads what arrived:

* **No export has arrived yet**: the runbook has not run, or its uploads are refused. The job output shows the HTTP error.
* **Sections missing / latest run is partial**: one cmdlet failed (usually a missing role) or the time budget ran out. The other checks keep using the last complete export.
* **Freshness warning**: nothing arrived for 48 hours. Check the schedule and that the managed identity still holds its role.

Then ask, for example, *"Is anyone auto-forwarding their mailbox outside the company?"* or open the Exchange Online checks in the catalog.

***

## How the upload works

Each run sends seven sections to `POST /api/v1/ingest/exchange.<section>` on your server, authenticated with the API key. A large section is split into parts of up to 1.5 MB, and every part carries the same run id, so ConfigView only uses a run once all of its parts have arrived; a re-sent part is ignored. ConfigView keeps 30 days of runs.

***

## Troubleshooting

| Symptom (job output) | Cause and fix |
| - | - |
| `ConfigViewApiKey is missing or is not a ConfigView API key` | The variable name or value is wrong, or it was created as a credential instead of a variable. |
| `Upload of … was redirected (HTTP 302 …)` | Something in front of ConfigView (a login gateway such as Cloudflare Access, Zscaler or a captive portal) intercepted the upload. Allow `/api/v1/ingest/*` on your ConfigView address through it without sign-in: the API key authenticates the upload. |
| `HTTP 403 … key_not_scoped_for_target` | The API key is missing that section's `Push: Exchange Online` scope (Step 1). |
| `HTTP 409 … table_missing` | Exchange Online is not selected on your Integrations page (Step 1). |
| `UnAuthorized` / `The role assigned to application … isn't supported` when connecting | Exchange.ManageAsApp (Step 3a) is missing, or Global Reader (3b) has not reached Exchange yet; allow up to an hour after assigning. |
| `The term 'Connect-ExchangeOnline' is not recognized` | The runbook is not using the runtime environment with ExchangeOnlineManagement (Step 2). |
| `mailbox_permissions (time budget … used up …)` | Raise `ConfigViewMaxMinutes`, within Azure Automation's 3-hour job limit. |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.