Skip to content
← All field notes

Field Notes · Sheet 01 / 30 · Identity and federation

Retiring AD FS with PowerShell

Two AD FS upgrades, one crash from a corrupted configuration, and about 30 relying parties moved to Entra ID, with PowerShell at every stage.

Bradley Baker · · 5 min read

Blueprint-style diagram of retiring AD FS with PowerShell. A staircase shows two farm upgrades between January 2021 and October 2025, from Windows Server 2012 R2 to 2016 to 2019 at farm behavior levels 1, 3 and 4. A two-server AD FS farm carries a crash note: configuration corrupted, recovered before any cutover. A scripted cutover arrow leads to Entra ID, where about 30 relying parties were rebuilt as enterprise apps. A PowerShell block exports every relying party trust with Get-AdfsRelyingPartyTrust.
Sheet 01 / 30 · Drawn by Bradley BakerFull size

Sheet 01 covers the end of AD FS in a 4,300-user global Microsoft 365 tenant: two farm upgrades between 2021 and 2025, one crash from a corrupted configuration, and about 30 relying parties moved to Entra ID. PowerShell did most of the work. This is the longer version, with the commands, the order they run in, and what Microsoft's documentation says about each step.

Two upgrades, three versions

The farm ran on two servers. It moved from Windows Server 2012 R2 to 2016, then to 2019, between January 2021 and October 2025.

Each version comes with a farm behavior level, a farm-wide setting that decides which features the farm can use. 2012 R2 is level 1, 2016 is level 3, and 2019 is level 4. A server running a newer version can join the farm, but the farm keeps running at the old level, in what Microsoft calls mixed mode, until you raise it. Raising the level also creates a new configuration database: AdfsConfigurationV3 at level 3 and AdfsConfigurationV4 at level 4.

Microsoft's upgrade guide lists prerequisites beyond the new servers themselves. The Active Directory schema has to be at level 85 or later for AD FS on Windows Server 2016, and 88 or later for 2019. The AD FS configuration and the federation servers need a backup. And the upgrade needs a planned end date, because a farm left in mixed mode for a long time can run into problems.

PowerShell

# The current level and the servers in the farm
Get-AdfsFarmInformation

# Remove each old server after uninstalling the AD FS role from it
Set-AdfsFarmInformation -RemoveNode "<old-server-FQDN>"

# Check, raise, and confirm
Test-AdfsFarmBehaviorLevelRaise
Invoke-AdfsFarmBehaviorLevelRaise
Get-AdfsFarmInformation
The raise, in the order Microsoft's upgrade guide runs it, once every server in the farm runs the new version.

The crash came first

The farm also crashed once, from a corrupted configuration. Recovery came before any cutover work, for two reasons. Every relying party still signed in through the farm, and its configuration held the claim rules each app would need once it was rebuilt in Entra ID.

The second reason tied the recovery to the plan. The export in the next section reads trusts and claim rules from a working farm, so the retirement plan depended on the farm it was retiring.

Four stages, each in PowerShell

The retirement ran on PowerShell from start to finish, in four stages.

1. Inventory

Get-AdfsRelyingPartyTrust with no parameters returns every relying party trust in the farm. The export on the sheet keeps three properties for each one: its name, its identifiers, and its issuance transform rules.

PowerShell

Get-AdfsRelyingPartyTrust |
  Select-Object Name, Identifier, IssuanceTransformRules |
  Export-Clixml .\rp-trusts.xml
Step 1 from the sheet: every relying party trust and its claim rules, in one export.

Export-Clixml writes the objects to a file that Import-Clixml can load again later. The claim rules come along as text, so the plan came from data instead of from clicking through the console.

2. Rebuild

Each app became an Entra enterprise app, built the same scripted way every time, and named business testers signed off on it.

Microsoft offers part of this in the admin center. Its AD FS application migration wizard creates the app registration and service principal, copies the identifier and reply URL, assigns the groups named in the trust's access control policy if they sync to Entra ID, and maps the claims Entra supports. It handles SAML apps only. It doesn't carry over the signing certificate or create Conditional Access policies, so those stay on the checklist either way.

3. Cutover

Sign-in activity on both sides showed when an app had really moved, and each cutover had a documented rollback path ready. Cutovers were planned around business hours, with application owners and user communication coordinated ahead of time.

On the Entra side, the evidence is the sign-in log for the new enterprise app. On the AD FS side, Microsoft's decommission guide points to the AD FS event logs and Microsoft Entra Connect Health, where any sign-in success or failure means something still depends on the farm.

A rebuilt app carries no traffic until the app itself is pointed at Entra ID. Microsoft's migration guide says the same about apps the wizard creates: the new app stays inactive until sign-in traffic is redirected to it, and rolling back before then means deleting it.

4. Decommission

Each trust came out only after its owner confirmed the app was stable and AD FS traffic for it had stopped. After the last relying party moved, the two-server farm was decommissioned.

For the farm itself, Microsoft's decommission guide asks for at least a week of Connect Health data on app usage in Entra ID, and no relying party traffic left on AD FS. Its steps then include, in order, an optional final backup, removing the load balancer and DNS entries, uninstalling the AD FS role starting with the secondary servers, deleting the SSL certificates, and re-imaging the servers.

Claim rules decide the effort

A trust that only needs a UPN is a rebuild. One with custom issuance rules or an external attribute store is a redesign. The AD FS application migration report in Entra flags those as Needs review or Additional steps required.

The report's rule checks show what makes a rule hard. These are the findings it raises on issuance rules, with what Entra offers instead.

The AD FS ruleReport findingIn Entra ID
Matches a claim with a regular expressionUNSUPPORTED_CONDITION_PARAMETERTransformations such as IfEmpty(), StartWith(), and Contains()
Rewrites a value with a regular expressionUNSUPPORTED_ISSUANCE_TRANSFORMATIONTransformations such as Extract(), Trim(), and ToLower()
Uses ADD to extend the incoming claim setUNSUPPORTED_ISSUANCE_CLASSSeveral claim transformations
Issues a restricted claimRESTRICTED_CLAIM_ISSUEDCan be issued, but its source can't change and no transformation applies
Reads from an attribute store other than Active DirectoryEXTERNAL_ATTRIBUTE_STORENo equivalent. Entra sources claims only from Active Directory or Entra ID.
AD FS claim rule patterns the migration report flags, and the Entra ID approach Microsoft's documentation gives for each.

Sheet 03 turns this into eight questions asked of every relying party before it moves. Questions 3 and 4 there are the claim-rule questions.

If you still run AD FS

Start with the question the post ended on: how many of your relying parties had a sign-in in the last 30 days? With the Connect Health agent for AD FS installed, the migration report answers it. The report lists only apps with sign-ins in that window and leaves out Microsoft relying parties such as Office 365, so an app missing from it is either idle or one of Microsoft's.

Then run the export. The plan for this retirement came from that file, not from the console.

Sources

  1. Upgrade an existing AD FS farm by using Windows Internal Databaselearn.microsoft.com
  2. Get-AdfsRelyingPartyTrustlearn.microsoft.com
  3. AD FS application migration to move AD FS apps to Microsoft Entra IDlearn.microsoft.com
  4. Active Directory Federation Services (AD FS) decommission guidelearn.microsoft.com

From the field reports

  • AD FS
  • Entra ID
  • PowerShell
  • SAML 2.0
  • Migration
This note on LinkedIn

Recon log

Findings 0/46

0/16 behavior and secret eggs. Hardware finds depend on the machine you visit from.

Hardware details are read by your browser and stay there. When you find an egg, only its name is counted, with no identifier attached, and nothing is counted if Global Privacy Control or Do Not Track is on.

Display0/7

  • Undiscovered (not found yet)

    rare

    Hint: Visit on a very large display.

  • Undiscovered (not found yet)

    uncommon

    Hint: Some monitors are wider than others.

  • Undiscovered (not found yet)

    uncommon

    Hint: Two screens are better than one.

  • Undiscovered (not found yet)

    rare

    Hint: Turn a monitor on its side.

  • Undiscovered (not found yet)

    common

    Hint: Pixels per inch matter.

  • Undiscovered (not found yet)

    uncommon

    Hint: Faster than 60 frames a second.

  • Undiscovered (not found yet)

    common

    Hint: Colors beyond sRGB.

Hardware0/11

  • Undiscovered (not found yet)

    rare

    Hint: Bring a flagship graphics card.

  • Undiscovered (not found yet)

    legendary

    Hint: A GPU that lives in a rack.

  • Undiscovered (not found yet)

    common

    Hint: A Mac with Apple silicon.

  • Undiscovered (not found yet)

    rare

    Hint: A Windows machine built for heavy work.

  • Undiscovered (not found yet)

    rare

    Hint: A Mac built for heavy work.

  • Undiscovered (not found yet)

    rare

    Hint: Two dozen threads or more.

  • Undiscovered (not found yet)

    rare

    Hint: Windows, but not on x86.

  • Undiscovered (not found yet)

    uncommon

    Hint: The year of the Linux desktop.

  • Undiscovered (not found yet)

    uncommon

    Hint: A laptop that boots straight into a browser.

  • Undiscovered (not found yet)

    uncommon

    Hint: No graphics card at all.

  • Undiscovered (not found yet)

    common

    Hint: A laptop you can poke.

Environment0/11

  • Undiscovered (not found yet)

    uncommon

    Hint: Visit while your battery is nearly flat.

  • Undiscovered (not found yet)

    uncommon

    Hint: Your browser has a setting for saving data.

  • Undiscovered (not found yet)

    uncommon

    Hint: Visit very late at night.

  • Undiscovered (not found yet)

    common

    Hint: Visit on a day off.

  • Undiscovered (not found yet)

    uncommon

    Hint: A browser with a lion on it.

  • Undiscovered (not found yet)

    common

    Hint: A browser with a fox on it.

  • Undiscovered (not found yet)

    uncommon

    Hint: An accessibility setting that calms things down.

  • Undiscovered (not found yet)

    uncommon

    Hint: Tell websites not to track you.

  • Undiscovered (not found yet)

    rare

    Hint: Visit from the Lone Star State.

  • Undiscovered (not found yet)

    common

    Hint: A newer version of HTTP.

  • Undiscovered (not found yet)

    rare

    Hint: A very narrow window.

Behavior0/7

  • Undiscovered (not found yet)

    uncommon

    Hint: Drag the edge of your browser window around.

  • Undiscovered (not found yet)

    uncommon

    Hint: Reach the bottom of the page quickly.

  • Undiscovered (not found yet)

    uncommon

    Hint: Try printing this page.

  • Undiscovered (not found yet)

    common

    Hint: Toggle the theme a few times.

  • Undiscovered (not found yet)

    rare

    Hint: Lose your connection.

  • Undiscovered (not found yet)

    rare

    Hint: Plug in a controller and press a button.

  • Undiscovered (not found yet)

    uncommon

    Hint: Sign the guestbook.

Secrets0/10

  • Undiscovered (not found yet)

    rare

    Hint: A famous cheat code.

  • Undiscovered (not found yet)

    common

    Hint: There's a terminal in here somewhere.

  • Undiscovered (not found yet)

    uncommon

    Hint: Ask the terminal about your machine.

  • Undiscovered (not found yet)

    uncommon

    Hint: Ask the terminal for root.

  • Undiscovered (not found yet)

    rare

    Hint: sudo, but make it a job offer.

  • Undiscovered (not found yet)

    rare

    Hint: The most dangerous command there is.

  • Undiscovered (not found yet)

    legendary

    Hint: Open your browser's developer console.

  • Undiscovered (not found yet)

    uncommon

    Hint: Click the logo. Then keep clicking.

  • Undiscovered (not found yet)

    legendary

    Hint: Type the 82nd's favorite word anywhere on the page.

  • Undiscovered (not found yet)

    legendary

    Hint: Find every egg that doesn't depend on your hardware.

Finds are saved in this browser only.

guest@bradbaker-it.com:~