Verify and Install Required Windows Features for SCCM DP, WSUS, and Exchange with PowerShell
- Christopher Hazlitt
- May 12
- 4 min read
If you've ever built out a Configuration Manager Distribution Point, stood up a WSUS server, or prepped a box for Exchange, you know the drill: there's a laundry list of Windows Features that must be installed before the role itself will even think about cooperating. Miss one, and you'll find out the hard way — usually halfway through an installer that bails with a vague prerequisite error and leaves you digging through logs.
The good news is that PowerShell makes this dead simple. With a short script, you can check every required feature on a server, see what's missing, and install just the ones you need — no more, no less.
The Naïve Approach
You may have come across (or written) something like this:
$Features = @("RDC","BITS","Web-Server","Web-Asp-Net45","Web-Common-Http",
"Web-Default-Doc","Web-Dir-Browsing","Web-Http-Errors",
"Web-Static-Content","Web-Http-Redirect","Web-Health",
"Web-Http-Logging","Web-Performance","Web-Stat-Compression",
"Web-Security","Web-Filtering","Web-Windows-Auth","Web-App-Dev",
"Web-ISAPI-Ext","Web-Mgmt-Tools","Web-Mgmt-Console",
"Web-Mgmt-Compat","Web-Metabase","Web-WMI","Web-Scripting-Tools")
foreach ($Feature in $Features) {
Add-WindowsFeature -Name $Feature
}This works, but it's blunt. It calls Add-WindowsFeature for every item, whether the feature is already installed or not. On a clean box that's fine. On a server that's been touched a few times, it's noisy, slow, and gives you no visibility into what was actually changed.
A Better Version: Check First, Install if Missing
A small tweak turns this into a proper verification-and-remediation script. We use Get-WindowsFeature to inspect the current state, then only install when the feature isn't already there:
# Required Windows Features for SCCM Distribution Point role
$Features = @("RDC","BITS","Web-Server","Web-Asp-Net45","Web-Common-Http",
"Web-Default-Doc","Web-Dir-Browsing","Web-Http-Errors",
"Web-Static-Content","Web-Http-Redirect","Web-Health",
"Web-Http-Logging","Web-Performance","Web-Stat-Compression",
"Web-Security","Web-Filtering","Web-Windows-Auth","Web-App-Dev",
"Web-ISAPI-Ext","Web-Mgmt-Tools","Web-Mgmt-Console",
"Web-Mgmt-Compat","Web-Metabase","Web-WMI","Web-Scripting-Tools")
foreach ($Feature in $Features) {
$state = Get-WindowsFeature -Name $Feature
if ($state.InstallState -eq "Installed") {
Write-Host "[OK] $Feature is already installed." -ForegroundColor Green
}
else {
Write-Host "[MISSING] $Feature — installing now..." -ForegroundColor Yellow
Install-WindowsFeature -Name $Feature | Out-Null
Write-Host "[DONE] $Feature installed." -ForegroundColor Cyan
}
}Run this and you get a clean, color-coded report of exactly what was already in place and what had to be added. Re-run it any time you want; it's idempotent.
The Feature List Above: SCCM Distribution Point
The 25 features in that array are the standard set required by a Configuration Manager Distribution Point — BITS for content transfer, RDC for differential compression of packages, and a healthy slice of IIS for the HTTP/HTTPS distribution endpoints. If you're adding a Pull DP or enabling PXE, you may need a couple more (notably WDS), but this list covers the common case.
Adapting It for WSUS
WSUS has its own list. Drop in this array and re-run the same loop:
$Features = @("UpdateServices","UpdateServices-WidDB","UpdateServices-Services",
"UpdateServices-RSAT","UpdateServices-API","UpdateServices-UI",
"Web-Server","Web-WebServer","Web-Common-Http","Web-Default-Doc",
"Web-Static-Content","Web-Http-Errors","Web-Http-Logging",
"Web-Performance","Web-Stat-Compression","Web-Dyn-Compression",
"Web-Security","Web-Filtering","Web-Windows-Auth",
"Web-App-Dev","Web-Net-Ext45","Web-Asp-Net45","Web-ISAPI-Ext",
"Web-ISAPI-Filter","Web-Mgmt-Tools","Web-Mgmt-Console")A note on the database: UpdateServices-WidDB uses the Windows Internal Database, which is fine for smaller environments. If you're running WSUS against a full SQL Server, swap that for UpdateServices-DB instead.
Adapting It for Exchange
Exchange is the heaviest of the three. Mailbox role on Exchange 2016/2019 needs something like:
$Features = @("Server-Media-Foundation","NET-Framework-45-Features",
"RPC-over-HTTP-proxy","RSAT-Clustering","RSAT-Clustering-CmdInterface",
"RSAT-Clustering-Mgmt","RSAT-Clustering-PowerShell","WAS-Process-Model",
"Web-Asp-Net45","Web-Basic-Auth","Web-Client-Auth","Web-Digest-Auth",
"Web-Dir-Browsing","Web-Dyn-Compression","Web-Http-Errors",
"Web-Http-Logging","Web-Http-Redirect","Web-Http-Tracing",
"Web-ISAPI-Ext","Web-ISAPI-Filter","Web-Lgcy-Mgmt-Console",
"Web-Metabase","Web-Mgmt-Console","Web-Mgmt-Service","Web-Net-Ext45",
"Web-Request-Monitor","Web-Server","Web-Stat-Compression",
"Web-Static-Content","Web-Windows-Auth","Web-WMI","Windows-Identity-Foundation")Always cross-check this against the prerequisites page for your exact Exchange version and cumulative update — Microsoft tweaks the list between CUs.
Tying It Together
If you maintain a few different role types, it's worth wrapping the whole thing in a function that takes a role name as a parameter:
function Install-RoleFeatures {
param(
[Parameter(Mandatory)]
[ValidateSet("DP","WSUS","Exchange")]
[string]$Role
)
$featureMap = @{
"DP" = @("RDC","BITS","Web-Server","Web-Asp-Net45", <# ...etc #> )
"WSUS" = @("UpdateServices","UpdateServices-WidDB", <# ...etc #> )
"Exchange" = @("Server-Media-Foundation","NET-Framework-45-Features", <# ...etc #> )
}
foreach ($Feature in $featureMap[$Role]) {
$state = Get-WindowsFeature -Name $Feature
if ($state.InstallState -eq "Installed") {
Write-Host "[OK] $Feature" -ForegroundColor Green
} else {
Write-Host "[MISSING] $Feature — installing..." -ForegroundColor Yellow
Install-WindowsFeature -Name $Feature | Out-Null
}
}
}
# Usage
Install-RoleFeatures -Role DPNow you've got one tool that handles all three scenarios, with a clear log of what each run did.
A Couple of Practical Tips
Run PowerShell as Administrator. Install-WindowsFeature needs it, and the script will fail silently on a non-elevated session.
Check the reboot flag. Install-WindowsFeature returns an object with a RestartNeeded property. For unattended automation, capture it and reboot when the loop is done rather than after each feature.
Use -IncludeManagementTools where appropriate. Some features have separate management consoles that don't come along for the ride by default.
Pre-stage your source files for offline servers. If your server can't reach Windows Update or a WSUS server, point Install-WindowsFeature at a mounted ISO with -Source <path>.
Wrapping Up
The pattern here is small but powerful: enumerate what you need, check what you have, fix the gap. It turns "did I remember to install everything?" from a manual checklist into a one-line answer. Once you've got the right feature list for your role, the same loop covers DPs, WSUS servers, Exchange boxes — and just about any other Windows role you'll throw at it.
Drop the script into your build automation, your post-deployment validation, or just keep it in your back pocket for the next time a role install throws a prerequisite error at 11 PM. Future-you will be glad you did.



Comments