Windows desktop console for migrating Exchange Server 2019 → mailcow. Inventories Exchange, transforms it into mailcow objects, pushes them over the mailcow API + SSH, and runs the mailbox mail copy.
Single ~155 KB .exe. No .NET SDK, no Visual Studio, no runtime install — builds with
the csc.exe already on every Windows box and runs on in-box .NET Framework 4.x.
30 read-only inventory categories (Get-* only) → a timestamped folder of CSVs,
SUMMARY.csv, and a log. With Generate mailcow artifacts on, it also writes the
mailcow\ folder: domains.json, mailboxes.json (size-derived quotas),
aliases.json, forwarding-filters.json, relayhost.json, forwarding-hosts.json,
mailbox-credentials.csv, postfix-extra.cf, sasl_passwd, and MIGRATION-REPORT.md.
Leave the FQDN blank to auto-connect when run on the Exchange server.
Connection config shared by tabs 3 and 4, with Test API and Test SSH buttons.
SSH uses the OS clients — ssh.exe/scp.exe for key auth, PuTTY plink/pscp for
password auth (host key auto-probed and pinned). No managed SSH dependency.
Pushes the transform output into mailcow. Dry-run until you tick Execute.
| Exchange | mailcow |
|---|---|
| Accepted domains | /add/domain |
| Mailboxes, shared mailboxes | /add/mailbox |
| Secondary addresses, distribution groups | /add/alias |
| Mailbox forwarding | /add/filter (sieve) |
| Send connector smarthost + credentials | /add/relayhost + per-domain /edit/domain (Routing → Sender-Dependent Transport) |
| Anonymous receive connector IPs | /add/fwdhost (Routing → Forwarding Hosts) |
| — | /add/dkim + generated DNS-CUTOVER.md |
Public IP ranges on receive connectors are flagged for review, never auto-trusted into
mynetworks. Non-routable domains (.local) and system mailboxes (HealthMailbox…)
are dropped automatically.
Prepare Exchange source — the only write to Exchange. Enables the IMAP services, creates (or password-resets) the migration account, and grants it FullAccess to every user mailbox. Requires running as Administrator.
Then either:
- mailcow Sync Jobs via API (recommended) — one job per mailbox; mailcow runs imapsync itself on a schedule, visible and retryable in the mailcow UI.
- imapsync once over SSH — uploads a script, runs it live, deletes it.
Both authenticate to Exchange as migrator@domain\targetalias with --exchange1.
This is the form Exchange 2019 actually accepts — --authuser1 does not work.
powershell -ExecutionPolicy Bypass -File .\build.ps1Copy both Mail_Migrate.exe and Mail_Migrate.exe.config to the Exchange server
and run from a local disk as an Exchange-privileged user, elevated.
- Exchange server with EMS/remoting (the app auto-connects:
RemoteExchange.ps1→ remote PSSession → snap-in) - .NET Framework 4.x, x64
- mailcow API key with read-write, and its IP allow-list including your source IP
- For the SSH steps: OpenSSH or PuTTY on PATH
- For imapsync-over-SSH:
imapsyncon the mailcow host, or Docker
IMAP carries mail only. Calendars, contacts, tasks and notes need a separate move
(SOGo import or an EWS→CalDAV/CardDAV tool). Public folders, transport rules,
litigation hold, retention policies and journaling have no mailcow equivalent — they
are inventoried and listed in MIGRATION-REPORT.md, not migrated.
Exchange passwords cannot be exported. Mailboxes are created with generated passwords
(written to mailbox-credentials.csv, reused as the imapsync destination auth) — reset
them after cutover.
- Inventory & Export on the Exchange server
- mailcow Server → Test API, Test SSH
- Settings Import → dry-run, read the log, then Execute
- Mail Migration → Prepare Exchange source, dry-run, then run
- Publish the records in
DNS-CUTOVER.mdand cut over MX
MAILCOW-SETUP.md covers standing up the mailcow host itself.
mailmigrate.settingsnext to the exe stores every field including passwords, in plaintext, so nothing is retyped between runs. Keep it private; delete it when done.- Sources are C# 5 — the in-box
csc.exeis pre-Roslyn, so no?., string interpolation, expression-bodied members, local functions, or auto-property initializers. Export-ExchangeInventory.ps1andPrepare-ExchangeForMigration.ps1are standalone script equivalents if you'd rather not run a binary.
MIT



