- One Windows Server 2016 host to test the commands on, plus a management workstation with Windows PowerShell 5.1.
- A domain account that is a local administrator on the target servers and has read rights in AD.
- RSAT Active Directory PowerShell module on the workstation (Get-ADComputer).
- WinRM enabled on the servers (TCP 5985/5986) or console/RDP access as a fallback.
- A folder for output, for example C:\Inventory\.
- Access to the official Microsoft Lifecycle Policy page for Windows Server 2016 (open it yourself and copy the dates — do not trust second-hand blog posts).

14393; patched servers still report 14393 with a higher revision.$PSVersionTable.PSVersion must show 5.1.x). PowerShell 2.0 will fail on the cmdlets below.Domain Users (or delegated read) for the AD query. Remote inventory needs WinRM, which is not enabled by default on all images.Create the working folder first:
New-Item -ItemType Directory -Path C:\Inventory -Force
What to do: Open the Microsoft Lifecycle Policy page for Windows Server 2016 in a browser, and copy three things into C:\Inventory\lifecycle.md: the mainstream end date, the extended end date, and the exact wording of the Extended Security Updates section. Then read the OS identity from a representative server.
Get-CimInstance Win32_OperatingSystem |
Select-Object Caption, Version, BuildNumber, InstallDate, LastBootUpTime |
Format-List
Example output (values differ per host — the build family is what matters):
Caption : Microsoft Windows Server 2016 Standard
Version : 10.0.14393
BuildNumber : 14393
InstallDate : 11/14/2017 09:12:44
LastBootUpTime : 03/02/2025 06:01:10
Why: the lifecycle dates are published per product and per edition, and the build number tells you the patch baseline. 10.0.14393 identifies Windows Server 2016; the last four digits never change, so a server that has never been patched and one patched last month look identical here — which is exactly why you record the hotfix level in Step 3. If you also maintain Windows client machines, the same "check the vendor page, then check the machine" method is described in How to Check When Your Windows PC Will Stop Receiving Updates.
How to check: run winver on the same server. The dialog must show "Windows Server 2016" and the same build number as BuildNumber above. In lifecycle.md you must have three filled lines, each with the date you checked the page — for example extended_end: <date from the page> (checked 2025-XX-XX). If a line is empty, the plan is not grounded and you must go back to the page; the exact date is not hard-coded in this guide on purpose — verify it in the official documentation.

What to do: pull every computer object whose operating system string mentions Windows Server 2016, then collect installed roles from each host over WinRM.
Get-ADComputer -Filter 'OperatingSystem -like "*Windows Server 2016*"' `
-Properties OperatingSystem, OperatingSystemVersion, LastLogonDate, DistinguishedName |
Select-Object Name, OperatingSystem, OperatingSystemVersion, LastLogonDate, DistinguishedName |
Sort-Object LastLogonDate |
Export-Csv C:\Inventory\ws2016-servers.csv -NoTypeInformation -Encoding UTF8
Example row you should see in the CSV:
"SRV-FS01","Windows Server 2016 Standard","10.0.14393","02/28/2025 21:40:11","CN=SRV-FS01,OU=FileServers,DC=contoso,DC=com"
Now collect the roles, because the migration path depends on them:
$servers = Import-Csv C:\Inventory\ws2016-servers.csv | Select-Object -ExpandProperty Name
Invoke-Command -ComputerName $servers -ErrorAction Continue -ScriptBlock {
[pscustomobject]@{
Server = $env:COMPUTERNAME
Roles = (Get-WindowsFeature | Where-Object Installed |
Select-Object -ExpandProperty Name) -join ';'
PSVersion = $PSVersionTable.PSVersion.ToString()
}
} | Select-Object Server, Roles, PSVersion |
Export-Csv C:\Inventory\ws2016-roles.csv -NoTypeInformation -Encoding UTF8
Why: a file server, a domain controller and an IIS web host cannot be replaced by the same procedure. Get-WindowsFeature lists installed roles and features on a server OS; capturing it once gives you the raw material for migration waves. Fill the "owner" and "business function" columns by hand — AD cannot tell you who cares about the machine.
| Role found in `Roles` | What to record before planning | Why it blocks a naive "rebuild the box" |
|---|---|---|
| `AD-Domain-Services` | Whether it holds FSMO roles (`netdom query fsmo`) | You must add a new DC, transfer roles, then demote — never rebuild in place |
| `DNS`, `DHCP` | Zone/scopes export, DHCP reservations | Clients lose name resolution or leases during cutover |
| `File-Services` | Share list, NTFS permissions, total size | Data copy window defines the maintenance window |
| `Web-Server` | Sites, bindings, certificates | TLS certificates must be re-issued or exported |
| Anything else | Vendor product name and support contact | Third-party software may not be certified on the new OS |
How to check: count the rows and compare with the AD query result.
(Import-Csv C:\Inventory\ws2016-servers.csv).Count
(Import-Csv C:\Inventory\ws2016-roles.csv).Count
Both numbers must match. If the second is smaller, some hosts did not answer over WinRM — see Troubleshooting. Then complete this checklist before moving on:
LastLogonDate.
What to do: confirm each server can still receive updates through your patch infrastructure, then pick one low-risk host (a test or secondary file server) and stand up its replacement, running the same commands against it.
Get-HotFix | Sort-Object InstalledOn -Descending |
Select-Object -First 5 HotFixID, Description, InstalledOn
Test-NetConnection <your-wsus-server> -Port 8530
Example output:
HotFixID Description InstalledOn
-------- ----------- -----------
KB50XXXXX Security Update 02/14/2025 00:00:00
KB50XXXXX Update 01/09/2025 00:00:00
ComputerName : <your-wsus-server>
RemotePort : 8530
TcpTestSucceeded : True
Why: the InstalledOn column proves the server is actually patched, not just "supposed to be". TcpTestSucceeded : True proves the path to the patch server is open, so any later failure is configuration, not the network. Run the identical Get-CimInstance Win32_OperatingSystem query on the replacement host: a different Version line (not 10.0.14393) confirms the new OS is in place before you move data.
How to check: on the pilot replacement, Get-WindowsFeature | Where-Object Installed must list at least the roles the old host had, and the application must pass its own smoke test (open the share, browse the site, query the database). Record the result as a line in your wave table:
| Wave | Servers | Criteria | Exit check |
|---|---|---|---|
| 1 | Test/secondary hosts | No FSMO roles, no customer-facing SLA | Replacement responds, `Get-HotFix` returns a recent KB |
| 2 | File, print, IIS | Data copy tested | Shares and bindings identical |
| 3 | DNS, DHCP, domain controllers | Maintenance window agreed | `netdom query fsmo` shows the new holder |
Problem. The AD query fails immediately on the workstation.
Get-ADComputer : The term 'Get-ADComputer' is not recognized as the name of a cmdlet, function, script file, or operable program.Install-WindowsFeature RSAT-AD-PowerShell; on Windows 10/11 run Add-WindowsCapability -Online -Name Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0. Reopen PowerShell and confirm with Get-Command Get-ADComputer — it must return the Cmdlet entry.Problem. The remote role inventory returns nothing for some servers.
Connecting to remote server SRV-FS01 failed with the following error message : WinRM cannot complete the operation. Verify that the specified computer name is valid, that the computer is accessible over the network, and that a firewall exception for the WinRM service is enabled and allows access from the current computer.Enable-PSRemoting -Force then winrm quickconfig. From the workstation verify with Test-WSMan SRV-FS01 — you should see wsmid, ProtocolVersion and ProductVersion in the output. Only then re-run the Invoke-Command block.Problem. A server reports a patch error instead of a hotfix list.
Windows Update failed with error 0x8024401CTest-NetConnection <your-wsus-server> -Port 8530; read the effective policy with gpresult /h C:\Inventory\rsop.html and look for WUServer under HKLM\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate; restart the client with net stop wuauserv then net start wuauserv, and re-run the detection.You now have three artefacts: vendor lifecycle dates with the check date, a CSV inventory of every Windows Server 2016 host with its roles and owner, and a validated patch path plus one successful pilot replacement. Next, assign each host to a wave in the table above, book maintenance windows, and — if the refresh also covers desktops — follow How to Install Windows 11 26H2: Step-by-Step Guide with Official ISO for the client side.
When exactly does Windows Server 2016 support end?
Microsoft publishes the mainstream end date and the extended end date on the product's Lifecycle Policy page. Open that page, copy both dates and the ESU wording into your plan with the date you checked — the exact value is deliberately not hard-coded here, so verify it in the official documentation rather than in a blog.
Do security updates stop completely after the end date?
Extended Security Updates (ESU) is the vendor programme that can keep an out-of-support product patched. Eligibility, delivery channel and pricing change per product and per year — confirm them on the official page before you budget anything.
Can I just rebuild the domain controller like a file server?
No. Add a new domain controller running a supported OS, wait for replication (repadmin /replsummary must show no failures), transfer the FSMO roles (netdom query fsmo before and after), and only then demote the old one.
How long will the whole migration take?
The audit in this guide takes 60–90 minutes. The migration itself depends on the roles you found in Step 2 and on your maintenance windows — there is no universal figure, so build the schedule from your own wave table.