Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
233 changes: 180 additions & 53 deletions docs/reference/providers/provider-exchangeonline.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,83 +10,194 @@ import ExoLeaverMailboxOffboarding from '@site/../examples/workflows/templates/e

## Summary

- **Module:** `IdLE.Provider.ExchangeOnline`
- **What it’s for:** Exchange Online mailbox configuration (type conversion, Out of Office, delegate permissions, mailbox info)
- **Targets:** Exchange Online via `ExchangeOnlineManagement` v3+ cmdlets (PowerShell 7+ compatible)
- **Identity keys:** UPN (recommended), SMTP address, mailbox identifiers (provider-specific)
| Item | Value |
| --- | --- |
| **Provider name** | `ExchangeOnlineProvider` |
| **Module** | `IdLE.Provider.ExchangeOnline` |
| **Provider role** | Messaging |
| **Targets** | Exchange Online via `ExchangeOnlineManagement` v3+ (PowerShell 7+) |
| **Status** | Built-in |
| **PowerShell** | PowerShell 7+ |

## When to use
---

## When to use this provider

Use this provider when your workflows need to manage **mailbox settings** in Exchange Online, for example:
### Use cases

- read mailbox info (type, primary SMTP, identifiers)
- apply a safe baseline at onboarding (verify mailbox + ensure expected type)
- convert mailbox type (e.g. user → shared for leavers)
- set Out of Office messages (internal/external) and audience
- converge delegate permissions (FullAccess, SendAs, SendOnBehalf)
- Read mailbox details (type, primary SMTP address, identifiers)
- Apply a safe baseline at onboarding (verify mailbox exists, ensure expected type)
- Convert mailbox type (e.g. user → shared for leavers)
- Set Out of Office messages (internal/external) and audience
- Converge delegate permissions (FullAccess, SendAs, SendOnBehalf)

Non-goals:
### Out of scope

- establishing the Exchange Online connection (host/runtime responsibility)
- managing identity objects (use AD / Entra ID providers for accounts)
- Establishing the Exchange Online session (host/runtime responsibility — see [Authentication](#authentication))
- Creating or deleting mailboxes (use Entra ID / AD providers for account lifecycle)
- Managing identity objects or directory attributes (use AD / Entra ID providers)

---

## Getting started

### Requirements

- `ExchangeOnlineManagement` v3.0+ module available on the execution host (supports PowerShell 7+)
- A host/runtime that establishes an Exchange Online session (delegated or app-only)
- Permissions for the mailbox operations you intend to run (conversion, OOO, permissions, etc.)
- **Module:** `ExchangeOnlineManagement` v3.0+ installed on the execution host
- **Session:** An Exchange Online session must be established **before** IdLE runs (call `Connect-ExchangeOnline` in your host/runtime)
- **Permissions:** The session identity must have rights for the mailbox operations you intend to run

> **PowerShell 7+ compatibility:** `ExchangeOnlineManagement` v3.0.0 and later support PowerShell 7+ on
> Windows, macOS, and Linux via REST-based cmdlets. The “Windows only” limitation in earlier versions
> applied to certificate-based app-only auth; the module itself runs cross-platform from v3.0 onwards.
> **PowerShell 7+ compatibility:** `ExchangeOnlineManagement` v3.0+ supports PowerShell 7+ on Windows, macOS, and Linux via REST-based cmdlets.

### Install (PowerShell Gallery)

```powershell
Install-Module IdLE.Provider.ExchangeOnline -Scope CurrentUser
```

### Import
### Import & basic check

```powershell
Import-Module IdLE.Provider.ExchangeOnline

# Create provider instance
$provider = New-IdleExchangeOnlineProvider
```

## Quickstart
The provider runs a one-time prerequisites check at construction and emits `Write-Warning` if the Exchange Online session is not established. See [Troubleshooting](#troubleshooting) if this fails.

Create provider and register it (example convention):
---

## Quickstart (minimal runnable)

```powershell
# 1) Establish Exchange Online session (host responsibility — outside IdLE)
Connect-ExchangeOnline -UserPrincipalName admin@contoso.com

# 2) Provider instance
$provider = New-IdleExchangeOnlineProvider

# 3) Provider map (alias used in workflow files)
$providers = @{
ExchangeOnline = New-IdleExchangeOnlineProvider
ExchangeOnline = $provider
}

# 4) Plan + execute
$plan = New-IdlePlan -WorkflowPath './workflow.psd1' -Request $request -Providers $providers
$result = Invoke-IdlePlan -Plan $plan -Providers $providers
```

---

## Authentication

This provider does **not** authenticate by itself.
This provider does **not** authenticate by itself. Your host/runtime must establish the Exchange Online session before IdLE runs.

- **Auth session name:** `ExchangeOnline`
- **Auth session options:** `@{ Role = 'Admin' }` (optional routing key)

Workflow steps reference the session via:

```powershell
With = @{
AuthSessionName = 'ExchangeOnline'
AuthSessionOptions = @{ Role = 'Admin' }
}
```

### Token requirements (delegated access)

When using delegated (user) authentication, mint the token for the Exchange Online resource:

```powershell
# Interactive (delegated) — requires user interaction at a browser prompt
$token = Get-MsalToken `
-ClientId '<app-id>' `
-TenantId '<tenant-id>' `
-Scopes 'https://outlook.office365.com/.default' `
-DeviceCode

Connect-ExchangeOnline -AccessToken $token.AccessToken -UserPrincipalName admin@contoso.com
```

> **Note:** `-DeviceCode` is interactive and requires a user to authenticate via a browser. For **automated/unattended** scenarios, use app-only authentication with a certificate.

App-only authentication example:

```powershell
Connect-ExchangeOnline -CertificateThumbprint '<thumbprint>' -AppId '<app-id>' -Organization '<tenant>.onmicrosoft.com'
```

Your host/runtime must establish the Exchange Online session and (optionally) route it via the AuthSessionBroker.
Mailbox steps typically reference that session via:
For **delegated** flows, the token's `scp` claim must include:
- `https://outlook.office365.com/Exchange.Manage` — full mailbox management (delegated)

- `AuthSessionName = 'ExchangeOnline'`
- `AuthSessionOptions = @{ Role = 'Admin' }` (optional routing key)
For **app-only** flows, the token's `roles` claim must include:
- `Exchange.ManageAsApp` — app-only/service principal access

> Keep credentials/secrets **out of workflow files**. Resolve them in the host/runtime and provide them via the broker.
> **Note:** The `.default` scope requests all delegated permissions pre-consented on the app registration. For app-only flows, ensure the `Exchange.ManageAsApp` app role is granted to your Entra ID application.

## Supported Step Types
:::warning
**Security**
- Do not pass secrets or access tokens in provider options or workflow files.
- Ensure credentials/tokens are not written to logs or events.
- The provider redacts token values from error messages automatically.
:::

Common step types using this provider include:
---

## Supported step types

| Step Type | Capability Required | Description |
| --- | --- | --- |
| `IdLE.Step.Mailbox.GetInfo` | `IdLE.Mailbox.Info.Read` | Read mailbox details |
| `IdLE.Step.Mailbox.EnsureType` | `IdLE.Mailbox.Type.Ensure` | Convert mailbox type |
| `IdLE.Step.Mailbox.EnsureOutOfOffice` | `IdLE.Mailbox.OutOfOffice.Ensure` | Configure Out of Office |
| `IdLE.Step.Mailbox.EnsureType` | `IdLE.Mailbox.Type.Ensure` | Convert mailbox type (User/Shared/Room/Equipment) |
| `IdLE.Step.Mailbox.EnsureOutOfOffice` | `IdLE.Mailbox.OutOfOffice.Ensure` | Configure Out of Office (enabled/disabled/scheduled) |
| `IdLE.Step.Mailbox.EnsurePermissions` | `IdLE.Mailbox.Permissions.Ensure` | Converge delegate permissions |

---

## Configuration

### Provider creation

- **Factory cmdlet:** `New-IdleExchangeOnlineProvider`

**Parameters**

- `-Adapter` — Internal use only (dependency injection for unit tests; do not set in production)

### Provider alias usage

```powershell
$providers = @{
ExchangeOnline = New-IdleExchangeOnlineProvider
}
```

- **Recommended alias:** `ExchangeOnline`
- **Default alias expected by mailbox steps:** `ExchangeOnline`

### Options reference

This provider has no admin-facing option bag. Authentication is handled by your runtime via the AuthSessionBroker.

---

## Operational behavior

- **Idempotency:** Yes — all `Ensure*` methods check current state before making changes; unchanged state = `Changed = $false`
- **Consistency model:** Depends on Exchange Online replication (eventual consistency for permission changes)
- **Throttling / rate limits:** Subject to Exchange Online service limits; no built-in retry — delegate retry to the host
- **Retry behavior:** None built-in; host/runtime is responsible for retry on transient failures

---

## Examples (canonical templates)

<CodeBlock language="powershell" title="examples/workflows/templates/exo-joiner.psd1">{ExoJoinerMailboxBaseline}</CodeBlock>

<CodeBlock language="powershell" title="examples/workflows/templates/exo-leaver.psd1">{ExoLeaverMailboxOffboarding}</CodeBlock>

### Delegate permissions example

```powershell
Expand All @@ -97,9 +208,9 @@ Common step types using this provider include:
Provider = 'ExchangeOnline'
IdentityKey = 'shared@contoso.com'
Permissions = @(
@{ AssignedUser = 'user1@contoso.com'; Right = 'FullAccess'; Ensure = 'Present' }
@{ AssignedUser = 'user2@contoso.com'; Right = 'SendAs'; Ensure = 'Present' }
@{ AssignedUser = 'leaver@contoso.com'; Right = 'FullAccess'; Ensure = 'Absent' }
@{ AssignedUser = 'user1@contoso.com'; Right = 'FullAccess'; Ensure = 'Present' }
@{ AssignedUser = 'user2@contoso.com'; Right = 'SendAs'; Ensure = 'Present' }
@{ AssignedUser = 'leaver@contoso.com'; Right = 'FullAccess'; Ensure = 'Absent' }
)
}
}
Expand All @@ -108,29 +219,45 @@ Common step types using this provider include:
Supported rights: `FullAccess`, `SendAs`, `SendOnBehalf`.
Each entry requires `AssignedUser` (UPN/SMTP), `Right`, and `Ensure` (`Present` or `Absent`).

## Configuration
### More examples

No admin-facing provider options.
- `examples/workflows/templates/entraid-exo-leaver.psd1` — cross-provider leaver (Entra ID + Exchange Online)

## Examples (canonical templates)
---

To keep provider documentation focused and consistent, this page embeds only the **canonical** Exchange Online templates:
## Troubleshooting

<CodeBlock language="powershell" title="examples/workflows/templates/exo-joiner.psd1">{ExoJoinerMailboxBaseline}</CodeBlock>
### Common problems

<CodeBlock language="powershell" title="examples/workflows/templates/exo-leaver.psd1">{ExoLeaverMailboxOffboarding}</CodeBlock>
- **`ExchangeOnlineManagement` module not installed**
→ Install it: `Install-Module ExchangeOnlineManagement -Scope CurrentUser`

## Troubleshooting
- **Provider warns "No active Exchange Online session"**
→ `Connect-ExchangeOnline` was not called before creating the provider.
Run `Connect-ExchangeOnline -UserPrincipalName admin@contoso.com` in your host/runtime first.

- **`Get-EXOMailbox` not found / module not imported**
→ Module is installed but not imported in this session: `Import-Module ExchangeOnlineManagement`

- **`Get-Mailbox` not recognized (session proxy cmdlet missing)**
→ No active Exchange Online session. Call `Connect-ExchangeOnline` before using the provider.

- **`Unauthorized` / 401 when using `-AccessToken`**
→ Token is not scoped for Exchange Online. Ensure you requested scopes for `https://outlook.office365.com/.default`, not `https://graph.microsoft.com/.default`.
Verify the token's `scp` claim contains `Exchange.Manage` or `Exchange.ManageAsApp`.

- **Access denied when changing mailbox settings**
→ The session identity must have the *Mail Recipients* management role (or Exchange Administrator) for mailbox changes, and *Recipient Management* for permission changes.

- **Module not found**: install `ExchangeOnlineManagement` on the execution host.
- **Not connected**: ensure the host establishes an Exchange Online session before IdLE runs.
- **Access denied**: the session identity must have permission to change mailbox settings.
- **OOO formatting issues**: use `MessageFormat = 'Html'` and validate HTML in a test mailbox first.
- **Permission changes not applied**: ensure the session identity has the *Mail Recipients* management role (or Exchange Administrator) required for `Add/Remove-MailboxPermission` and `Add/Remove-RecipientPermission`.
- **OOO formatting issues**
→ Use `MessageFormat = 'Html'` and validate HTML in a test mailbox first. The provider normalizes HTML before comparing for idempotency.

## Scenarios (link-only)
- **Permission changes not visible immediately**
→ Exchange Online replication is eventually consistent; allow a few minutes for changes to propagate.

Cross-provider orchestration examples are valuable, but should not be embedded in a single provider reference page.
Keep them as **link-only** and collect them on a central Examples/Scenarios page:
### What to collect for support

- `examples/workflows/templates/entraid-exo-leaver.psd1`
- IdLE version, `IdLE.Provider.ExchangeOnline` module version
- `ExchangeOnlineManagement` module version
- Redacted error message (the provider automatically redacts tokens from error output)
- Whether using delegated or app-only auth (without sharing credentials)
Comment thread
blindzero marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,13 @@ function Test-IdleExchangeOnlinePrerequisites {
Checks if the Exchange Online prerequisites are available.

.DESCRIPTION
Validates that the ExchangeOnlineManagement PowerShell module is available.
This module is required for all Exchange Online provider operations.
Validates that the ExchangeOnlineManagement PowerShell module is available and that
a working Exchange Online session exists in the current runspace.

Three checks are performed in order:
1. Module availability — ExchangeOnlineManagement must be installed.
2. Module import — Get-EXOMailbox must be discoverable (module imported in session).
3. Session established — Get-Mailbox must be available (Connect-ExchangeOnline was called).

This function does not throw and returns a structured result object
that can be used by the provider to emit warnings or by provider methods
Expand Down Expand Up @@ -43,6 +48,27 @@ function Test-IdleExchangeOnlinePrerequisites {
$notes += 'The ExchangeOnlineManagement PowerShell module is required for all Exchange Online provider operations.'
$notes += 'Install via: Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser'
}
else {
# Module is available — now verify key cmdlets are accessible.
# Get-EXOMailbox is a native module cmdlet (always present after Import-Module).
# Its absence means the module has not been imported into this session yet.
$exoMailboxCmd = Get-Command -Name 'Get-EXOMailbox' -ErrorAction SilentlyContinue
if ($null -eq $exoMailboxCmd) {
$missingRequired += 'Get-EXOMailbox'
$notes += "The ExchangeOnlineManagement module is installed but 'Get-EXOMailbox' is not available in this session."
$notes += 'Ensure the module is imported: Import-Module ExchangeOnlineManagement'
}

# Get-Mailbox is a session proxy cmdlet — only available after Connect-ExchangeOnline.
# Its absence means no active Exchange Online session exists in this runspace.
$getMailboxCmd = Get-Command -Name 'Get-Mailbox' -ErrorAction SilentlyContinue
if ($null -eq $getMailboxCmd) {
$missingRequired += 'ExchangeOnlineSession'
$notes += "No active Exchange Online session detected ('Get-Mailbox' is not available)."
$notes += 'Establish a session before using the provider: Connect-ExchangeOnline -UserPrincipalName admin@contoso.com'
$notes += "For delegated access, acquire a token scoped to 'https://outlook.office365.com/.default' and pass it via -AccessToken."
}
}

$isHealthy = ($missingRequired.Count -eq 0)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,13 @@ function New-IdleExchangeOnlineProvider {
[object] $Adapter
)

# Check prerequisites and emit warnings if required components are missing
# Run prerequisites check at construction for early diagnostic output only.
# The actual gate check is deferred to the first real operation so the provider
# can recover if Connect-ExchangeOnline is called after the provider is created.
Write-Verbose "Provider.ExchangeOnline.Init.Start: Checking prerequisites (ProviderName=ExchangeOnlineProvider)"
$prereqs = Test-IdleExchangeOnlinePrerequisites
Write-Verbose "Provider.ExchangeOnline.Prerequisites.ModuleImport: ExchangeOnlineManagement module available=$(-not ($prereqs.MissingRequired -contains 'ExchangeOnlineManagement'))"
Write-Verbose "Provider.ExchangeOnline.CommandAvailability: Get-EXOMailbox=$(-not ($prereqs.MissingRequired -contains 'Get-EXOMailbox')) ExchangeOnlineSession=$(-not ($prereqs.MissingRequired -contains 'ExchangeOnlineSession'))"
if (-not $prereqs.IsHealthy) {
foreach ($missing in $prereqs.MissingRequired) {
Write-Warning "ExchangeOnline provider prerequisite check: Required component '$missing' is not available."
Expand All @@ -85,6 +90,7 @@ function New-IdleExchangeOnlineProvider {
Write-Warning "ExchangeOnline provider prerequisite check: $note"
}
}
Write-Verbose "Provider.ExchangeOnline.Init.End: IsHealthy=$($prereqs.IsHealthy)"

if ($null -eq $Adapter) {
$Adapter = New-IdleExchangeOnlineAdapter
Expand All @@ -103,6 +109,8 @@ function New-IdleExchangeOnlineProvider {
$isRealAdapter = ($this.Adapter.PSObject.TypeNames -contains 'IdLE.ExchangeOnlineAdapter')

if ($isRealAdapter) {
# Re-check prerequisites on each operation so the provider can recover
# if Connect-ExchangeOnline is called after the provider was created.
$prereqCheck = Test-IdleExchangeOnlinePrerequisites
if (-not $prereqCheck.IsHealthy) {
$missingList = $prereqCheck.MissingRequired -join ', '
Expand Down
Loading