{"id":50798497,"url":"https://github.com/mardahl/exchange-soa-manager","last_synced_at":"2026-06-12T17:01:03.836Z","repository":{"id":364244028,"uuid":"1267027692","full_name":"mardahl/Exchange-SOA-Manager","owner":"mardahl","description":"Terminal UI for Exchange Online Source of Authority (SOA) management - mailboxes, groups, contacts and the tenant-wide default. Single-file PowerShell, PS 5.1+.","archived":false,"fork":false,"pushed_at":"2026-06-12T07:35:06.000Z","size":40,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-12T09:20:55.631Z","etag":null,"topics":["exchange-hybrid","exchange-online","microsoft-entra","microsoft-graph","powershell","source-of-authority","terminal-ui","tui"],"latest_commit_sha":null,"homepage":null,"language":"PowerShell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mardahl.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-12T06:47:41.000Z","updated_at":"2026-06-12T07:35:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/mardahl/Exchange-SOA-Manager","commit_stats":null,"previous_names":["mardahl/exchange-soa-manager"],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/mardahl/Exchange-SOA-Manager","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mardahl%2FExchange-SOA-Manager","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mardahl%2FExchange-SOA-Manager/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mardahl%2FExchange-SOA-Manager/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mardahl%2FExchange-SOA-Manager/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mardahl","download_url":"https://codeload.github.com/mardahl/Exchange-SOA-Manager/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mardahl%2FExchange-SOA-Manager/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34253942,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["exchange-hybrid","exchange-online","microsoft-entra","microsoft-graph","powershell","source-of-authority","terminal-ui","tui"],"created_at":"2026-06-12T17:01:02.886Z","updated_at":"2026-06-12T17:01:03.831Z","avatar_url":"https://github.com/mardahl.png","language":"PowerShell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Exchange SOA Manager\n\n[![CI](https://github.com/mardahl/Exchange-SOA-Manager/actions/workflows/ci.yml/badge.svg)](https://github.com/mardahl/Exchange-SOA-Manager/actions/workflows/ci.yml)\n[![PowerShell](https://img.shields.io/badge/PowerShell-5.1%20%7C%207%2B-5391FE?logo=powershell\u0026logoColor=white)](#requirements)\n[![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-555)](#requirements)\n[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)\n[![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md)\n\nA portable, single-file PowerShell **terminal UI** for managing **Source of Authority (SOA)** in Exchange Online / Microsoft Entra hybrid environments.\n\nSwitch Exchange attribute management between **on-premises** and **cloud** for mailboxes, convert the object-level SOA of **groups** and **contacts**, and control the **tenant-wide default** for new dir-synced mailboxes - all from one keyboard-driven console app. Built for the road toward decommissioning the *Last Exchange Server*.\n\n```\n Exchange SOA Manager  v1.0.0          ● EXO admin@contoso.com   ● Graph admin@contoso.com\n  1 Mailboxes   2 Groups   3 Contacts   4 Organization   5 Log\n 340 of 340 mailboxes   3 selected   filter:All   sort:Name↑\n sel Name                       Email                              Type           SOA\n [■] Anna Andersen              anna.andersen@contoso.com          UserMailbox    ● Cloud\n [ ] Anna Clausen               anna.clausen@contoso.com           UserMailbox    ● On-prem\n [■] Astrid Dahl                astrid.dahl@contoso.com            UserMailbox    ● Cloud\n ...\n Spc select  A all  N none  / find  F filter  S sort  C to cloud  O to on-prem  E export\n```\n\n\u003e **Try it in 10 seconds, no tenant required:** `.\\SOA-Manager.ps1 -Demo`\n\n---\n\n- [Why](#why)\n- [Features](#features)\n- [Quick start](#quick-start)\n- [Requirements](#requirements)\n- [What the states mean](#what-the-states-mean)\n- [Files the tool writes](#files-the-tool-writes)\n- [Caveats](#caveats)\n- [Contributing](#contributing) · [Security](#security) · [Changelog](CHANGELOG.md)\n\n## Why\n\nMicrosoft now supports moving the SOA for Exchange attributes (and whole directory objects) to the cloud, which is the key to finally decommissioning the *Last Exchange Server*. The classic community tooling only covers user mailboxes. This tool covers all four surfaces:\n\n| Surface | Mechanism | Connection |\n|---|---|---|\n| **Mailboxes** | `Set-Mailbox -IsExchangeCloudManaged $true/$false` | Exchange Online PowerShell |\n| **Groups** | `PATCH /groups/{id}/onPremisesSyncBehavior` (`isCloudManaged`) | Microsoft Graph |\n| **Contacts** | `PATCH /contacts/{id}/onPremisesSyncBehavior` (`isCloudManaged`) | Microsoft Graph |\n| **Organization** | `Set-OrganizationConfig -ExchangeAttributesCloudManagedByDefault` / `-ExchangeAttributesServerManagedByDefault` | Exchange Online PowerShell |\n\n## Features\n\n- **Pure PowerShell TUI** - no WinForms, no WPF, no DLLs, works over SSH and in any VT-capable terminal (Windows Terminal, conhost, iTerm2, ...)\n- **Windows PowerShell 5.1 and PowerShell 7+** (PS 7 includes macOS/Linux)\n- **Safety first**\n  - JSON **attribute backup** written before every conversion (proxy addresses, custom attributes, forwarding, moderation, and more for mailboxes; full directory object for groups/contacts)\n  - **Group rollback safety check**: detects cloud-only members that would be lost when sync takes over again, per Microsoft guidance\n  - Typed confirmation (`ROLLBACK` / `ENABLE` / `DISABLE`) for hazardous operations\n  - Tenant-wide changes show Microsoft's GAL warning before proceeding\n- **Batch operations** with live progress, per-item results, and failed items kept selected for retry\n- **Search** (`/` live filter), **status filters** (Cloud / On-prem / Pending), **sorting**, **multi-select**\n- **CSV export** of any view; **CSV/TXT bulk import** to select objects by identity\n- **Pending state tracking** - rolled-back groups/contacts show as `◐ Pending` until the sync client takes over\n- **Timestamped log file** plus an in-app log viewer\n- **Demo mode** (`-Demo`) with generated data - explore the UI without touching a tenant\n- **Lazy connections** - Exchange Online and Graph are only connected when a tab needs them; modules are offered for install (CurrentUser) on demand\n\n## Quick start\n\n```powershell\n# Explore the UI with fake data (no tenant access, nothing installed)\n.\\SOA-Manager.ps1 -Demo\n\n# The real thing\n.\\SOA-Manager.ps1\n```\n\n| Parameter | Effect |\n|---|---|\n| `-Demo` | Generated sample data, no connections, no changes |\n| `-Ascii` | Plain ASCII glyphs for legacy consoles / raster fonts |\n| `-NoDisconnect` | Keep the EXO + Graph sessions alive on exit |\n\n### Keys\n\n| Key | Action |\n|---|---|\n| `Tab` / `1-5` | Switch tab |\n| `↑ ↓ PgUp PgDn Home End` | Navigate |\n| `Space` | Toggle selection (`A` all, `N` none) |\n| `/` | Live search (`Enter` keep, `Esc` clear) |\n| `F` | Cycle status filter All → Cloud → On-prem → Pending |\n| `S` / `D` | Cycle sort column / flip direction |\n| `C` | Convert selection to **cloud-managed** SOA |\n| `O` | Convert selection back to **on-prem-managed** SOA |\n| `E` / `I` | Export view to CSV / import CSV-TXT selection |\n| `R` | Reload from the service |\n| `Esc` | Cancel a load in progress |\n| `W` | Disconnect sessions |\n| `?` | Help · `Q` quit |\n\n## Requirements\n\n### Modules (installed on demand, CurrentUser scope)\n\n- [`ExchangeOnlineManagement`](https://www.powershellgallery.com/packages/ExchangeOnlineManagement) - mailboxes + organization config\n- [`Microsoft.Graph.Authentication`](https://www.powershellgallery.com/packages/Microsoft.Graph.Authentication) - groups + contacts (lightweight; the full Graph SDK is **not** required)\n\n### Roles \u0026 permissions\n\n| Task | Requirement |\n|---|---|\n| Mailbox SOA + org config | **Exchange Administrator** (or Global Administrator) |\n| Group / contact SOA | **Hybrid Identity Administrator** + admin consent for the Graph scopes below |\n\nGraph scopes requested: `Group.Read.All`, `GroupMember.Read.All`, `User.Read.All`, `Group-OnPremisesSyncBehavior.ReadWrite.All`, `OrgContact.Read.All`, `Contacts-OnPremisesSyncBehavior.ReadWrite.All`.\n\n\u003e The `*-OnPremisesSyncBehavior` scopes require **admin consent** in the tenant. See [Configure Group SOA](https://learn.microsoft.com/entra/identity/hybrid/how-to-group-source-of-authority-configure#grant-permission-to-apps).\n\n\u003e **Using PIM?** Activate the relevant role **before** connecting - the tool shows the required role on each tab and in the sign-in prompt. If you activate a role *after* connecting, press `W` to disconnect, then reconnect to get a fresh token.\n\n### Sync client versions (Microsoft prerequisites)\n\n- Mailbox attribute SOA: Entra **Connect Sync ≥ 2.5.190.0** (or Cloud Sync)\n- Group SOA: Connect Sync **≥ 2.5.76.0** or Cloud Sync **≥ 1.1.1370.0**\n\n## What the states mean\n\n| Badge | Meaning |\n|---|---|\n| `● On-prem` | Dir-synced, managed on-premises (classic hybrid) |\n| `● Cloud` | SOA transferred - editable in EXO / Entra |\n| `◐ Pending` | Rolled back to on-prem; waiting for the sync client to take the object over again |\n\nMailbox conversions only change **Exchange attribute** management - the identity itself stays synced from AD. Group/contact conversions move the **whole object's** SOA. After rolling anything back, run a sync cycle (`Start-ADSyncSyncCycle`) to complete the takeover.\n\n## Files the tool writes\n\n| Location | Content |\n|---|---|\n| `SOA-Manager_\u003ctimestamp\u003e.log` | Session log (also viewable on the Log tab) |\n| `SOA-Backups/Backup_\u003ctype\u003e_\u003ctimestamp\u003e.json` | Pre-conversion attribute snapshots |\n| `SOA-Exports/\u003ctab\u003e_\u003ctimestamp\u003e.csv` | View exports |\n\n## Caveats\n\n- Do **not** offboard (migrate) a mailbox to on-premises while `IsExchangeCloudManaged` is `$true` - set it back to `$false` first, otherwise sync breaks.\n- Rolling back a group/contact completes only after the next Connect/Cloud Sync cycle.\n- After updating mailbox attributes on-premises, Microsoft recommends waiting a full sync cycle **plus 24h** before flipping that mailbox to cloud-managed.\n- Contacts that were rolled back disappear from the Contacts tab after a refresh until the sync client re-claims them (Graph cannot distinguish them from cloud-born contacts).\n- Enabling the tenant-wide cloud default while still provisioning mailboxes on-premises hides those new mailboxes from the EXO GAL (Microsoft warning - shown in-app too).\n\n## References\n\n- [Cloud-based management of Exchange attributes (Microsoft Learn)](https://learn.microsoft.com/exchange/hybrid-deployment/enable-exchange-attributes-cloud-management)\n- [Configure Group Source of Authority](https://learn.microsoft.com/entra/identity/hybrid/how-to-group-source-of-authority-configure)\n- [Configure User / Contact Source of Authority](https://learn.microsoft.com/entra/identity/hybrid/how-to-user-source-of-authority-configure)\n\n## Contributing\n\nBug reports and PRs are welcome - see [CONTRIBUTING.md](CONTRIBUTING.md) for\nthe ground rules (single file, PS 5.1 compatible, safety UX) and a scripted\ntmux recipe for testing the TUI. CI enforces PSScriptAnalyzer and parse checks\non both PowerShell engines. Release notes live in [CHANGELOG.md](CHANGELOG.md).\n\n## Security\n\nNo telemetry, no credential handling (auth is delegated to MSAL via the\nofficial modules). Logs/backups/exports contain directory data - treat them\naccordingly. See [SECURITY.md](SECURITY.md) for the full policy and how to\nreport vulnerabilities privately.\n\n## Credits\n\nInspired by [codeandersen's Exchange-SOA-Conversion-tool](https://github.com/codeandersen/Exchange-SOA-Conversion-tool) (WinForms, user mailboxes only). This project is an independent rewrite that adds groups, contacts, the tenant-wide default switch, safety checks, and a portable terminal UI.\n\n## License\n\nMIT - see [LICENSE](LICENSE).\n\nProvided as-is, without warranty. Test in a non-production tenant first.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmardahl%2Fexchange-soa-manager","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmardahl%2Fexchange-soa-manager","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmardahl%2Fexchange-soa-manager/lists"}