🔑

Rotating Microsoft 365 DKIM to 2048-bit in PowerShell, and the gotcha that leaves half the job undone

How to rotate Microsoft 365 DKIM keys and upgrade from 1024-bit to 2048-bit with one cmdlet, why you have to run it twice, and what to do when both selectors already say 2048.

If you've just read our Google Workspace DKIM piece and felt your blood pressure rise about signing gaps and lowered TTLs, here's the good news: Microsoft 365 does this properly. It rotates DKIM with genuine zero downtime. One cmdlet, no DNS surgery, no gap.

There is exactly one trap, and it's a good one, because it leaves you convinced the job is finished when it's half done. We'll get to it.

Why M365 gets to be smooth

Microsoft 365 publishes two DKIM selectors for every domain, selector1 and selector2, as two CNAME records pointing at Microsoft-hosted keys:

selector1._domainkey.yourdomain.com.  IN  CNAME  selector1-yourdomain-com._domainkey.yourtenant.onmicrosoft.com.
selector2._domainkey.yourdomain.com.  IN  CNAME  selector2-yourdomain-com._domainkey.yourtenant.onmicrosoft.com.

Microsoft holds the private keys; you never copy a TXT value or paste a 2048-bit blob into anything. Because two selectors always exist, rotation is just a switch: M365 signs with one selector while the other sits ready, and when you rotate it generates a fresh key on the idle selector and starts signing with that one. The previously-active key stays valid long enough for mail already in flight to verify. Nothing gaps. The "two selectors live at once" design is simply how M365 was built.

Microsoft 365 keeps two DKIM selectors live at once, so a rotation is a handover, not an outage. The cmdlet does the work; the only way to fumble it is to run it one time too few.

Why upgrade from 1024 to 2048

Same reasons as anywhere. 1024-bit RSA is below the current M3AAWG standard of 2048-bit, and deliverability tools increasingly grumble about it. Newer M365 tenants already default to 2048, but anything provisioned before Microsoft flipped that default is quietly still on 1024 until you rotate it. And the same hygiene rule applies: M3AAWG says rotate at least every six months whatever your key size, because a key that's never rotated is a key that's had years to leak. The upgrade and the rotation are the same cmdlet, so you may as well do both at once.

The commands

Never run commands you found on the internet, ours included, against a live tenant without checking them first. Every cmdlet below is Microsoft's own. Search the cmdlet name and confirm the exact syntax in the official Exchange Online PowerShell docs before you paste anything into an admin session.

Connect to Exchange Online:

Connect-ExchangeOnline -UserPrincipalName [email protected]

Check what you've actually got before you change anything:

Get-DkimSigningConfig -Identity yourdomain.com | Format-List Name,Enabled,Status,Selector1KeySize,Selector2KeySize,Selector1CnameValue,Selector2CnameValue

Read the two KeySize values. If either says 1024, you've got upgrading to do. Then rotate to 2048:

Rotate-DkimSigningConfig -Identity yourdomain.com -KeySize 2048

If the config comes back disabled, enable it first with Set-DkimSigningConfig -Identity yourdomain.com -Enabled $true, and make sure both CNAMEs above actually exist at your DNS host, a sloppily set-up custom domain sometimes never had them.

The gotcha, straight from Microsoft's own docs

Here's the part that catches everyone. Run that rotate command and you'd reasonably assume both selectors are now 2048-bit. They aren't. Microsoft's own documentation says it plainly:

Upgrading the key size to 2048 only upgrades the selector that isn't currently active. After key rotation has taken place, you need to run the command again to upgrade the key size of the other selector.

So one Rotate-DkimSigningConfig -KeySize 2048 upgrades the idle selector and switches signing to it. Your other selector, the one that was active, is still sitting on its old 1024-bit key. You've upgraded exactly half your DKIM and the config looks healthy enough that you'd never notice. To finish the job you run the identical command a second time:

Rotate-DkimSigningConfig -Identity yourdomain.com -KeySize 2048

But, crucially, not straight away. Rotation isn't instant. The new private key takes up to four days (96 hours) before M365 actually starts signing with it. Fire the second rotation immediately and you risk rotating off a key that was never used, or muddling which selector is active. Run it once, wait the four days, confirm the new key is live, then run it again for the other selector. Patience is the whole technique.

"But both my selectors already say 2048, can I rotate now?"

This is the right question to ask, and the answer is yes, with a clarification.

If your check comes back like this:

Enabled          : True
Status           : Valid
Selector1KeySize : 2048
Selector2KeySize : 2048

then there is nothing left to upgrade. Both selectors are already at the modern key size, the two-runs-of--KeySize-2048 dance doesn't apply to you, and running it again wouldn't make the keys any more 2048 than they already are.

What you can still do, and should, is rotate for freshness. Both selectors being 2048 tells you the key size is current; it tells you nothing about how long those particular keys have been signing your mail. If it's been more than six months, rotate. For a pure hygiene rotation you don't need -KeySize at all, and you don't need to run it twice, you just want a fresh key:

Rotate-DkimSigningConfig -Identity yourdomain.com

That generates a new key on the idle selector and switches signing to it, with the same seamless handover and the same 96-hour activation window. One run, done. Diarise the next one for six months out. The twice-over routine is only for the size upgrade; routine freshness rotation is a single command.

Verify

After any rotation, confirm where you landed:

Get-DkimSigningConfig -Identity yourdomain.com | Format-List Selector1KeySize,Selector2KeySize,Status

Both sizes should read 2048 and Status should be Valid. Then check it from the outside, the way a receiving server sees it: run the domain through the DKIM checker and the Microsoft 365 checker, and confirm a real test send shows dkim=pass at 2048-bit. PowerShell telling you it's fine and the live DNS actually agreeing are two different facts, and only the second one is the one that delivers your mail.

Our take

M365 DKIM is the easy one. The cmdlet does the hard part, the handover is genuinely seamless, and the only way to get it wrong is to stop one rotation too early and leave a 1024-bit selector hiding in plain sight. Run it, wait four days, run it again, verify from outside. And whether you're upgrading or just keeping keys fresh, put the next rotation in the calendar, because the six-month habit is the actual win, the 2048 upgrade is just the day you finally started.

With DKIM clean on both your platforms, the payoff is enforcement: taking DMARC from p=none to p=reject is what turns all this authentication into actual protection.

What to do

  1. Connect-ExchangeOnline, then Get-DkimSigningConfig and read both KeySize values.
  2. If either is 1024: Rotate-DkimSigningConfig -KeySize 2048, wait 96 hours, then run it again for the second selector.
  3. If both are already 2048: skip the upgrade. For freshness, run a plain Rotate-DkimSigningConfig once if it's been six months.
  4. Verify 2048 and dkim=pass from outside with the DKIM checker and M365 checker, and diarise the next rotation.
← Back to all articles