top of page

Verify and Install Required Windows Features for SCCM DP, WSUS, and Exchange with PowerShell

  • Writer: Christopher Hazlitt
    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 DP

Now 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

Rated 0 out of 5 stars.
No ratings yet

Add a rating*
bottom of page