The first time an engineer RDPs into a Windows Server Core box, the reaction is always the same: a black screen, one cmd.exe window, no Start menu, no explorer.exe, no Server Manager. It feels broken. It is not — it is the most operationally correct way to run Windows Server, and the missing desktop is the point. Server Core ships roughly half the binaries of the Desktop Experience install, takes a materially smaller slice of the monthly cumulative-update churn, and removes an entire class of “someone clicked the wrong checkbox in a wizard” misconfiguration. What it costs you is the muscle memory of pointing and clicking: every task you used to do in a window — install a role, read the event log, format a disk, change an IP — still exists, but now it is a cmdlet, a remote console, or a browser tab pointed at Windows Admin Center (WAC).
This article is the working model for administering a headless fleet end to end. You bring a box up with sconfig (or an unattend file), lock down WinRM and move PowerShell remoting onto an HTTPS listener, drive day-two work three ways — native PowerShell, the classic RSAT / Server Manager MMC tools pointed remotely, and a hardened Windows Admin Center gateway — and keep the whole thing patched and hardened without ever installing a GUI. We cover the workflows that trip people up specifically because the GUI is gone: drivers with pnputil instead of Device Manager, event logs as an object pipeline instead of Event Viewer, storage with the Storage module instead of Disk Management, and the Features on Demand trick that lets a stubborn app that “needs the GUI” run on Core anyway.
Everything here targets Windows Server 2019 / 2022 / 2025, domain-joined unless stated otherwise. One thing to internalise first: the Server Core install option is chosen at setup and is a one-way door — you cannot convert a running Core box to Desktop Experience (that in-place switch was removed after Server 2012 R2). Pick it deliberately, provision it as cattle, and never plan to “add the GUI later.”
What problem this solves
Most Windows Server roles do not need an interactive desktop. A domain controller, a DNS/DHCP server, a file server, a Hyper-V host, an IIS worker — none of them require a human sitting at explorer.exe. Yet teams keep deploying the Desktop Experience “to be safe,” then inherit a patch surface, a reboot cadence, and a memory/disk footprint they never needed. That install carries Edge, the shell, media components, Paint, and dozens of services that run by default — every one a binary that can carry a CVE you are now obligated to patch and reboot for.
What breaks without the headless skill set is not the servers — it is the operators. A team that has only administered Windows through wizards treats a Core box as an emergency: they RDP in, stare at the black screen, and either bolt the GUI onto the next build or quietly standardise on Desktop Experience, losing all the benefit. The fix is not to avoid Core; it is to learn the three remote surfaces (PowerShell remoting, RSAT/MMC, Windows Admin Center) so well that the missing local GUI is a non-event. Once you administer servers remotely and declaratively — which you should at scale anyway — Core simply forces the good habit and removes the temptation to hand-edit a production box.
Who hits this: anyone running a Windows Server estate past a handful of machines, anyone building golden images at scale, anyone on constrained edge hardware where the extra GUI footprint matters, and any security team shrinking attack surface and reboot count across a fleet. The payoff is measurable — fewer binaries, patches, reboots, and GUI-driven mistakes — for the price of a weekend learning the remote workflows this article lays out.
To frame the whole surface before the deep dive, here is what changes when the desktop disappears, and where the replacement lives:
| GUI tool you lose | Headless replacement | Runs from | Notes |
|---|---|---|---|
| Server Manager (local dashboard) | sconfig, then PowerShell + remote Server Manager |
Local console / admin workstation | Server Manager still works — pointed at the Core box remotely |
| Server Manager “Add Roles” wizard | Install-WindowsFeature |
Remote PowerShell | One line; add -IncludeManagementTools |
| Device Manager | pnputil, Get-PnpDevice |
Local or remote | Drivers on the live OS, fully scriptable |
| Event Viewer | Get-WinEvent |
Remote PowerShell / WAC | Object pipeline; filter across many nodes |
| Disk Management | Storage module (Get-Disk, New-Partition) |
Remote PowerShell / WAC | End-to-end init → partition → format |
| Performance Monitor | Get-Counter |
Remote PowerShell / WAC | Live counters without perfmon |
Local Users and Groups (lusrmgr.msc) |
Get-LocalUser, RSAT via computer mgmt |
Remote MMC / PowerShell | Domain accounts via AD tools |
Services (services.msc) |
Get-Service / Set-Service, RSAT |
Remote PowerShell / MMC | JEA-friendly for delegated restart |
| Certificates MMC | Cert:\ PSDrive, certlm.msc remote |
Remote PowerShell / MMC | Manage LocalMachine\My etc. |
| The whole desktop, at a glance | Windows Admin Center | Any browser | GUI-equivalent over the same WinRM plumbing |
Learning objectives
By the end of this article you can:
- Explain why Server Core is the right default — quantified in attack surface, patch footprint, and reboot cadence — and name the handful of cases where it is the wrong call.
- Bring a Server Core box from first boot to domain-joined and addressed using
sconfigfor one-offs and an unattend.xml for repeatable, hands-off provisioning. - Stand up and harden remote management:
Enable-PSRemoting, a WinRM HTTPS listener on port 5986 bound to a real certificate, and the removal of the HTTP listener and Basic auth. - Drive all three remote surfaces fluently — PowerShell remoting (
Enter-PSSession,Invoke-Command), RSAT + Server Manager / MMC pointed at the remote box, and Windows Admin Center installed as a gateway with connections and extensions. - Perform every missing-GUI day-two task headless: roles/features, drivers, event logs, performance, storage, users, services, and certificates.
- Use Features on Demand (FoD) — the Server Core App Compatibility Feature on Demand (
ServerCore.AppCompatibility) — to run apps that expect GUI DLLs, without installing the full Desktop Experience. - Troubleshoot a box you cannot see: WinRM/cert/Kerberos failures, a hung service, a full disk, a driver that will not load — each with the exact command to confirm and fix.
Prerequisites & where this fits
You should be comfortable at a PowerShell prompt (variables, the pipeline, Get-Help), understand the basics of Active Directory (a domain, a DC, an OU, a computer account, Kerberos vs NTLM at a high level), and know what a certificate and an EKU are. Familiarity with TCP ports and the Windows firewall helps, as does having administered some Windows Server before — this is not a “what is a server” article.
This sits in the Windows Server operations track and is upstream of almost everything else in it. Once you can run a headless box, the role-specific deep dives follow naturally: Configuration Management for Windows Server with PowerShell DSC and Ansible is how you make the configuration you set here declarative and enforced; Hardening Windows Server and Building a Reliable WSUS Patch Pipeline is how you patch the fleet you just made leaner; and Eliminating Static Service Credentials with gMSA and Windows LAPS is how the service accounts on these boxes stop being a liability. If your Core boxes are domain controllers, Building an AD DS Forest the Right Way: Deployment, FSMO, and a Tiered Admin Model is the design they slot into. Managing the same fleet from the cloud is Onboarding Servers to Azure Arc: Connected Machine Agent, Service Principals & Bulk Enrollment.
A quick map of the three management surfaces, because choosing the right one per task is half the skill:
| Surface | What it is | Best for | Where it runs | Auth |
|---|---|---|---|---|
sconfig |
Menu wrapper over local commands | First-boot bring-up, break-glass at the console | Local Core console only | Interactive local logon |
| Native PowerShell remoting | Enter-PSSession / Invoke-Command over WinRM |
Automation, fleet fan-out, scripted day-two | Admin workstation → node(s) | Kerberos / cert / NTLM |
| RSAT + MMC / Server Manager | Classic Windows admin tools, pointed remotely | Familiar GUI muscle memory; one box at a time | Admin workstation (Win10/11 or mgmt server) | Kerberos |
| Windows Admin Center | Browser-based, gateway-hosted console | GUI-equivalent for a team; dashboards; roles | Gateway server → nodes; browser to gateway | Kerberos SSO / Entra ID (via Arc) |
Core concepts
A few mental models make every later section obvious.
Server Core is a smaller Windows, not a crippled one. It runs the same kernel, drivers, roles, and PowerShell as Desktop Experience. What it omits is the presentation layer: explorer.exe, the MMC snap-in hosts, Edge, media, and the GUI-only consoles. Because those are absent, so are their servicing components — which is why the patch and reboot footprint shrinks. Everything a role does is present; only the way you look at it locally changed.
You administer it from somewhere else. The design assumption is that the box has no human at its console after provisioning; all three surfaces above are remote-first. Even Windows Admin Center, which looks like a local console, is a web app on a gateway server that reaches your nodes over WinRM — no agent on the managed node. Your management plane is centralised, auditable, and does not require RDP to every box.
WinRM is the transport under almost everything. WinRM (Windows Remote Management), Microsoft’s WS-Management implementation, is the pipe — PowerShell remoting and Windows Admin Center both ride it. It listens on HTTP port 5985 and HTTPS port 5986. In a domain, Kerberos authenticates and encrypts the payload even over the HTTP listener, so “HTTP” here is not plaintext — but you still move to HTTPS (5986) for uniformity, for non-domain and cross-forest hops, and so tooling negotiates TLS the same way everywhere.
The GUI tools didn’t die — they moved to your workstation. RSAT (Remote Server Administration Tools) is the package of MMC consoles and PowerShell modules that lets a Windows 10/11 client (or management server) manage remote servers. ADUC, DNS Manager, DHCP, Failover Cluster Manager, Hyper-V Manager — all run on your desktop and connect to the Core box. You never lost Event Viewer; you lost the ability to run it on the server locally. Run it on your workstation, point it at the server, done.
Features on Demand bridge the app-compat gap. Some third-party software links against GUI DLLs (mmc.exe, shell parts, MDAC/OLE components) that Core omits, and refuses to install or run. Rather than reach for Desktop Experience, you add the Server Core App Compatibility Feature on Demand — an optional payload that restores those specific components (and tools like mmc.exe, perfmon.exe, Event Viewer, and Explorer bits) without turning the box into a full desktop. It is the pressure-release valve that keeps you on Core.
The vocabulary in one table
Pin down every moving part before the deep sections; the glossary at the end repeats these for lookup.
| Term | One-line definition | Where it lives | Why it matters headless |
|---|---|---|---|
| Server Core | Windows Server install option with no desktop shell | Chosen at setup (one-way) | The whole premise; smaller, leaner, patched less |
| Desktop Experience | Full-GUI install option | Chosen at setup | The thing you are deliberately not using |
sconfig |
Text-menu wrapper over config commands | Runs at the local console | First-boot bring-up without memorising cmdlets |
| WinRM | WS-Man remote-management service | Service on every node | Transport for remoting and WAC |
| PowerShell remoting | Running commands on a remote box over WinRM | Enter-PSSession/Invoke-Command |
Primary automation surface |
| RSAT | Remote admin tools (MMC + modules) | On your Win10/11 workstation | Classic GUI, pointed at the server |
| Windows Admin Center (WAC) | Browser console served by a gateway | Gateway server + browser | Team-friendly GUI over WinRM |
| JEA | Just Enough Administration — constrained endpoints | Session config on the node | Delegate ops without full admin |
| Features on Demand (FoD) | Optional on-demand OS payloads | Added post-install | App-compat without Desktop Experience |
| RSAT tools = MMC snap-ins | ADUC, DNS, DHCP, Hyper-V Manager, etc. | Workstation | Manage roles you can’t see locally |
| Unattend.xml | Answer file for hands-off setup | Applied at install/sysprep | Repeatable, no-console provisioning |
Why Server Core: attack surface, patching, and footprint
The argument for Server Core is engineering, not aesthetics. Three axes matter.
Attack surface. Every component you do not install cannot carry a vulnerability you must patch, nor be a foothold for an attacker. Server Core omits the shell (explorer.exe), Edge, Media Player and codecs, the browser stack, and dozens of GUI services and listeners. Fewer running services means fewer open ports and processes to compromise; fewer binaries means fewer CVEs land on your monthly plate at all.
Patch and reboot footprint. Cumulative updates ship fixes across the OS. If a month’s update fixes something in the browser, shell, or a media component, a Core box that lacks that component does not need to apply (or reboot for) that portion. Over a fleet and a year, that is real maintenance-window time recovered and change-risk avoided. The precise number varies month to month — the mechanism is deterministic: absent components generate no servicing.
Disk and memory footprint. A Core install is meaningfully smaller on disk and boots with a smaller working set. On one VM that is a rounding error; across hundreds of VMs, or on constrained edge hardware, the aggregate disk saved and memory freed per host add up to fewer or cheaper hosts.
The trade is that local administration collapses to a single PowerShell window — fine, because at scale you administer remotely and declaratively anyway. The honest comparison:
| Dimension | Server Core | Desktop Experience | Practical impact |
|---|---|---|---|
| Local GUI | None (one console) | Full shell + MMC | Core forces remote/declarative admin |
| Installed binaries | ~Half | Full | Fewer CVEs, smaller patch surface |
| Monthly reboot exposure | Lower (absent components) | Higher | Recovered maintenance-window time |
| Disk footprint | Smaller | Larger (several GB more) | Matters at fleet/edge scale |
| RDP experience | Black screen, one cmd window | Full desktop | Not a problem once you manage remotely |
| App compatibility | Most server workloads; FoD for the rest | Everything, including GUI installers | Rare vendor GUI-installer dependency |
| Convertible to the other option | No (one-way) | No | Choose deliberately at setup |
| Right for… | DC, DNS/DHCP, file, Hyper-V, IIS, middleware | GUI-only third-party apps, RDS session hosts | Match install option to workload |
When to pick which, as a decision table:
| If the workload is… | Choose | Because |
|---|---|---|
| Domain controller / DNS / DHCP | Server Core | No local GUI needed; smallest surface for a Tier-0 asset |
| File server / DFS / scale-out | Server Core | Storage + shares are fully manageable headless |
| Hyper-V host / S2D node | Server Core | Recommended; less to patch on the hypervisor |
| IIS / .NET / most middleware | Server Core | Install-WindowsFeature + remote IIS management |
| App with a hard GUI-installer dependency | Core + FoD, or Desktop as last resort | Try App Compatibility FoD before conceding the GUI |
| Remote Desktop Session Host (interactive desktops) | Desktop Experience | Users literally need the desktop |
| A vendor appliance that mandates full GUI and refuses support otherwise | Desktop Experience (and push the vendor) | Support contract trumps preference |
The reflex “install the GUI to be safe” is the expensive default. You inherit a patch surface and reboot cadence you did not need, on every box, forever. Default to Core, keep the App Compatibility FoD in your back pocket, and reserve Desktop Experience for the genuine session-host and hard-GUI-installer cases.
Initial configuration with sconfig and unattended provisioning
On first boot, Server Core drops you into a console with sconfig available — a text-menu wrapper over the same commands you would otherwise script. It is genuinely useful for a one-off bring-up or break-glass at the console, and nothing else. In Server 2022/2025 sconfig is a PowerShell script (it launches PowerShell); on older builds it is a .cmd. The menu:
sconfig
1) Domain/Workgroup 8) Network Settings
2) Computer Name 9) Date and Time
3) Add Local Administrator 10) Telemetry settings
4) Configure Remote Management 11) Windows Activation
5) Windows Update Settings 12) Log Off User
6) Download and Install Updates 13) Restart Server
7) Remote Desktop 14) Shut Down Server
15) Exit to command line
The options you actually use at first boot, and the cmdlet each wraps (so you can script it instead):
sconfig option |
What it does | Underlying cmdlet / command |
|---|---|---|
| 2) Computer Name | Rename the server (needs reboot) | Rename-Computer -NewName <name> -Restart |
| 1) Domain/Workgroup | Join a domain or workgroup | Add-Computer -DomainName <dom> -Credential ... |
| 8) Network Settings | Static IP / DNS servers | New-NetIPAddress, Set-DnsClientServerAddress |
| 4) Configure Remote Management | Enable/allow WinRM + ping | Enable-PSRemoting, firewall rules |
| 7) Remote Desktop | Allow RDP (to the console) | Set-ItemProperty on Terminal Server keys |
| 6) Download and Install Updates | Trigger Windows Update | UsoClient / PSWindowsUpdate module |
| 5) Windows Update Settings | Auto/manual update policy | Registry / policy |
| 11) Windows Activation | Activate / set KMS key | slmgr |
Beyond a lab, do not hand-drive sconfig. Bake configuration into an unattend.xml so the box arrives named, addressed, and domain-joined with zero console interaction. Select the Core image in the Microsoft-Windows-Setup pass, and set identity in the specialize pass:
<settings pass="specialize">
<component name="Microsoft-Windows-Shell-Setup"
processorArchitecture="amd64"
language="neutral"
xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State">
<ComputerName>SRV-CORE-01</ComputerName>
</component>
<component name="Microsoft-Windows-UnattendedJoin"
processorArchitecture="amd64" language="neutral">
<Identification>
<JoinDomain>corp.example.com</JoinDomain>
<Credentials>
<Domain>corp.example.com</Domain>
<Username>svc-domainjoin</Username>
<Password>__INJECTED_AT_DEPLOY__</Password>
</Credentials>
</Identification>
</component>
</settings>
Never commit join credentials to the unattend file in source control. Inject them at deploy time from your secrets store (a CI variable group, a vault, or an offline-domain-join blob). For a truly credential-free join, use
djoin /provisionto generate a blob on a DC and reference it from<OfflineIdentification>in the answer file. For golden images, runsysprep /oobe /generalize /unattend:unattend.xmlso each clone gets a unique SID and machine identity.
To configure an already-running box from another machine, PowerShell beats sconfig. Set a static IP, point DNS at your domain controllers, and join the domain — all remotely if WinRM is up, or at the local console for the very first hop:
# Static IP + DNS (run at the console on first boot, or remotely once WinRM works)
New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress 10.20.0.21 `
-PrefixLength 24 -DefaultGateway 10.20.0.1
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.20.0.10,10.20.0.11
# Rename and join in one reboot
Rename-Computer -NewName "SRV-CORE-01"
Add-Computer -DomainName corp.example.com -Credential (Get-Credential) -Restart
Find the interface alias first if you are not sure of it — Get-NetAdapter lists them. A common first-boot gotcha: the default interface is often named Ethernet but can be Ethernet0/Ethernet 2 on some hypervisors, and using the wrong alias silently fails.
Enabling and securing PowerShell remoting and WinRM over HTTPS
In a domain, Enable-PSRemoting is effectively already done — WinRM runs and the firewall rule is open for the domain profile out of the box on Windows Server. The real work is moving off the default HTTP listener (port 5985) to an authenticated, encrypted HTTPS listener (port 5986) backed by a real certificate, then closing the HTTP path.
Kerberos already encrypts PowerShell remoting traffic on a domain HTTP listener, so HTTP-in-domain is not plaintext — this is a common misconception. You still want HTTPS for non-domain endpoints, for cross-forest and workgroup management, and so WAC and other tooling can negotiate TLS uniformly. Standardise on 5986; keep 5985 only if a specific tool requires it, and even then scope it tightly.
First, get a certificate with the server’s FQDN in the subject (or a SAN) and the Server Authentication EKU. With an enterprise CA, request it from the host itself:
# Request a Server Authentication cert from an enterprise CA (WebServer template)
Get-Certificate -Template "WebServer" `
-DnsName "srv-core-01.corp.example.com" `
-CertStoreLocation Cert:\LocalMachine\My
Then create the HTTPS listener bound to that certificate’s thumbprint and open the firewall for 5986:
$cert = Get-ChildItem Cert:\LocalMachine\My |
Where-Object { $_.Subject -match "srv-core-01" } | Select-Object -First 1
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * `
-HostName "srv-core-01.corp.example.com" `
-CertificateThumbPrint $cert.Thumbprint -Force
New-NetFirewallRule -DisplayName "WinRM HTTPS (5986)" -Direction Inbound `
-Protocol TCP -LocalPort 5986 -Action Allow
Once HTTPS works fleet-wide, disable the HTTP listener so nothing silently falls back to it, and harden the WinRM service to reject Basic auth and unencrypted traffic:
# Remove the HTTP listener AFTER confirming HTTPS connectivity end to end
Get-ChildItem WSMan:\localhost\Listener |
Where-Object { $_.Keys -contains "Transport=HTTP" } |
Remove-Item -Recurse
Set-Item WSMan:\localhost\Service\AllowUnencrypted $false
Set-Item WSMan:\localhost\Service\Auth\Basic $false
The WinRM authentication and transport settings, what each does, and the secure value:
WinRM setting (WSMan:\localhost\...) |
Controls | Default | Secure value | Note |
|---|---|---|---|---|
Service\AllowUnencrypted |
Allow unencrypted traffic | $false |
$false |
Keep off; never enable to “make it work” |
Service\Auth\Basic |
Basic (username/password) auth | $false |
$false |
Sends creds; disable |
Service\Auth\Kerberos |
Kerberos auth | $true |
$true |
The domain default; keep on |
Service\Auth\Negotiate |
Negotiate (Kerberos→NTLM) | $true |
$true (domain) |
NTLM fallback for non-Kerberos |
Service\Auth\Certificate |
Client-certificate auth | $false |
$true for workgroup |
Enables cert-based auth to non-domain nodes |
Service\Auth\CredSSP |
Credential delegation (double-hop) | $false |
Prefer off | Use RBCD/resource-based delegation instead |
Client\TrustedHosts |
Hosts to trust without Kerberos | empty | Named FQDNs only | Never * |
Listener Transport=HTTP (5985) |
Cleartext-capable listener | present | Removed in prod | Remove after HTTPS proven |
Listener Transport=HTTPS (5986) |
TLS listener | absent | Present, CA-issued cert | The standard target |
Verify the connection from your admin workstation before you rely on it. Test-WSMan confirms the listener answers; Enter-PSSession proves end-to-end auth over TLS:
Test-WSMan -ComputerName srv-core-01.corp.example.com -UseSSL
Enter-PSSession -ComputerName srv-core-01.corp.example.com -UseSSL -Credential (Get-Credential)
For a non-domain / workgroup node, there is no Kerberos to provide mutual authentication, so you must explicitly trust the host by FQDN and use HTTPS (or client-certificate auth). Scope TrustedHosts to named hosts — never *:
# On the admin/management host — enumerate specific FQDNs, never use '*'
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "edge-core-09.dmz.example.com" -Concatenate
Delegated administration with Just Enough Administration (JEA)
The mistake most teams make on headless fleets is granting operators full local admin “just to restart a service.” JEA (Just Enough Administration) fixes this and is more valuable on Core than anywhere else, because there is no local GUI to fall back on — the JEA endpoint is the interface an operator gets. JEA exposes a constrained PowerShell endpoint where the user runs as a temporary virtual account with only the cmdlets you whitelist, and every command is transcribed to disk.
JEA has two artifacts: a role capability file (.psrc) defining what is allowed, and a session configuration file (.pssc) mapping AD groups to roles. Build the role first:
# On the managed node: scaffold the module + role capability
$module = "$env:ProgramFiles\WindowsPowerShell\Modules\OpsRoles"
New-Item "$module\RoleCapabilities" -ItemType Directory -Force | Out-Null
New-PSRoleCapabilityFile -Path "$module\RoleCapabilities\ServiceOperator.psrc" `
-VisibleCmdlets 'Restart-Service','Get-Service','Get-WinEvent',
@{ Name='Stop-Service'; Parameters=@{ Name='Name'; ValidateSet='Spooler','W3SVC' } }
That ValidateSet is the point: a service operator may stop only Spooler or W3SVC — not WinRM, not the cluster service. Now bind the role to an AD group and register it as a constrained endpoint:
New-PSSessionConfigurationFile -Path .\JeaServiceOps.pssc `
-SessionType RestrictedRemoteServer `
-RunAsVirtualAccount `
-TranscriptDirectory "C:\JEA-Transcripts" `
-RoleDefinitions @{ 'CORP\ServiceOperators' = @{ RoleCapabilities = 'ServiceOperator' } }
Register-PSSessionConfiguration -Name "ServiceOps" `
-Path .\JeaServiceOps.pssc -Force
A member of CORP\ServiceOperators connects to the constrained endpoint and can do exactly what the role allows and nothing else — Get-Command returns only the whitelisted cmdlets, whoami shows the virtual account, and Stop-Service -Name WinRM is rejected. Windows Admin Center can also target a JEA endpoint, so the browser experience inherits the same guardrails:
Enter-PSSession -ComputerName srv-core-01.corp.example.com -UseSSL `
-ConfigurationName "ServiceOps" -Credential (Get-Credential)
The JEA session-configuration knobs that matter most:
.pssc setting |
What it does | Typical value | Why |
|---|---|---|---|
SessionType |
Base language mode of the endpoint | RestrictedRemoteServer |
Locks the session to no-language + visible cmdlets |
RunAsVirtualAccount |
Run commands as an ephemeral admin identity | $true |
Operator never holds standing admin |
RunAsVirtualAccountGroups |
Scope the virtual account’s group membership | e.g. specific role group | Least privilege for the ephemeral account |
RoleDefinitions |
Map AD groups → role capabilities | @{ 'CORP\Grp' = @{ RoleCapabilities='...' } } |
The delegation itself |
TranscriptDirectory |
Where every session is recorded | C:\JEA-Transcripts |
Audit trail; ship to SIEM |
LanguageMode (implied) |
PowerShell language restriction | NoLanguage (via type) |
Blocks arbitrary script |
Managing roles, features, and drivers without a GUI
Everything Server Manager’s “Add Roles and Features” wizard did is a one-liner. Roles and features install with Install-WindowsFeature, run remotely against any node:
Install-WindowsFeature -Name Web-Server,Web-Mgmt-Service `
-ComputerName srv-core-02.corp.example.com -IncludeManagementTools
The -IncludeManagementTools switch brings in the remote-administration cmdlet modules and RSAT snap-in support for that role — without dragging in the desktop GUI. That distinction is what keeps Core lean: you get the management surface, not explorer.exe. To audit what is installed across the fleet, fan out over a session:
$s = New-PSSession -ComputerName srv-core-01,srv-core-02 -UseSSL
Invoke-Command -Session $s {
Get-WindowsFeature | Where-Object Installed | Select-Object Name
}
The feature-management cmdlets you actually use, and what each is for:
| Cmdlet | Purpose | Key parameters | Notes |
|---|---|---|---|
Install-WindowsFeature |
Add a role/feature | -Name, -IncludeManagementTools, -Restart, -Source |
-Source points at a WIM for offline payload |
Uninstall-WindowsFeature |
Remove a role/feature | -Name, -Remove |
-Remove deletes the payload from disk (FoR) |
Get-WindowsFeature |
List / audit features | -ComputerName, Installed filter |
Fleet inventory |
Get-WindowsCapability |
List FoD capabilities | -Online, -Name |
For Features on Demand payloads |
Add-WindowsCapability |
Add a FoD payload | -Online, -Name, -Source |
App-compat FoD lives here |
Enable-WindowsOptionalFeature |
Enable an optional feature (DISM-backed) | -Online, -FeatureName |
e.g. some legacy features |
Features on Demand vs Features on Removal. If you
Uninstall-WindowsFeature -Name X -Remove, the payload is deleted from the running image, shrinking it further; reinstalling later needs a-Source. Conversely, some capabilities are not present at all by default and must be added on demand (the App Compatibility FoD, RSAT capabilities on clients, .NET 3.5). Know which direction you are going, and stage a-Source(a mounted install WIM or an offline FoD ISO) so an air-gapped box can still add payloads.
Drivers, without Device Manager, use pnputil — fully scriptable — and the PnpDevice cmdlets for querying. Stage a driver package and confirm it landed:
# Add a driver package to the LIVE OS, then list third-party drivers
pnputil /add-driver C:\drivers\vendor-nic\*.inf /install
pnputil /enum-drivers
# Query device state as objects
Get-PnpDevice -Class Net | Select-Object Status, FriendlyName, InstanceId
The driver-management surface headless, tool by tool:
| Task | Command | Target | Notes |
|---|---|---|---|
| Add a driver to the running OS | pnputil /add-driver *.inf /install |
Live host | Replaces “have disk” in Device Manager |
| List installed 3rd-party drivers | pnputil /enum-drivers |
Live host | Shows OEM##.inf published names |
| Remove a driver package | pnputil /delete-driver oem12.inf /uninstall |
Live host | Use the published OEM name |
| Enumerate device status | Get-PnpDevice |
Live host | Objects, filterable by -Class/-Status |
| Enable/disable a device | Enable-PnpDevice / Disable-PnpDevice |
Live host | By InstanceId |
| Inject a driver into an OFFLINE image | Add-WindowsDriver (DISM) |
Mounted WIM/VHD | For image builds, not the live host |
On Server Core,
Add-WindowsDriver(DISM) targets offline images;pnputiltargets the running OS. Usepnputilfor live hosts. For Hyper-V and storage hosts, pin firmware/driver bundles in your image pipeline so a manualpnputilinstall never silently diverges nodes in the same cluster — driver drift between cluster members is a real outage source, covered in Patching Failover Clusters with Cluster-Aware Updating and Stretch Clusters via Storage Replica.
Remote GUI tooling: Server Manager, RSAT, and the MMC consoles
Native PowerShell is the automation surface; RSAT is how you keep the classic GUI muscle memory when you want it. RSAT is a set of MMC consoles and PowerShell modules that runs on a Windows 10/11 workstation (added as Features on Demand / optional features) or on a management server, and connects to your Core boxes. You did not lose Event Viewer or DNS Manager or Hyper-V Manager — you lost the ability to run them on the server. Run them on your desktop, point them at the server, done.
Install RSAT on a Windows 11 admin workstation:
# List available RSAT capabilities, then add the ones you need
Get-WindowsCapability -Online -Name RSAT* |
Select-Object Name, State
Add-WindowsCapability -Online -Name "Rsat.ActiveDirectory.DS-LDS.Tools~~~~0.0.1.0"
Add-WindowsCapability -Online -Name "Rsat.Dns.Tools~~~~0.0.1.0"
Add-WindowsCapability -Online -Name "Rsat.ServerManager.Tools~~~~0.0.1.0"
Server Manager itself works against Core — you run Server Manager on your workstation (or a management server), “Add other servers to manage,” and it dashboards the remote Core boxes, letting you add roles and read status remotely. The key insight beginners miss: Server Manager was never on the Core box; it always managed other servers, and Core is just one of those “other servers.”
The classic consoles and what they manage, all pointed at the remote Core node:
| MMC console / tool | Manages | RSAT capability / source | Connect by |
|---|---|---|---|
| Server Manager | Roles, features, status dashboard | Rsat.ServerManager.Tools |
Add server → FQDN |
Active Directory Users and Computers (dsa.msc) |
AD objects | Rsat.ActiveDirectory.DS-LDS.Tools |
Targets the domain, not the box |
DNS Manager (dnsmgmt.msc) |
DNS zones/records | Rsat.Dns.Tools |
Connect → server FQDN |
DHCP (dhcpmgmt.msc) |
Scopes/leases | Rsat.DHCP.Tools |
Add server |
| Hyper-V Manager | VMs on a Core host | Rsat.Hyper-V.Tools / feature |
Connect to server |
| Failover Cluster Manager | Clusters/roles | Rsat.FailoverCluster.Management.Tools |
Connect to cluster |
Computer Management (compmgmt.msc) |
Services, disks, event logs, shares | Built into Windows | Action → Connect to another computer |
Event Viewer (eventvwr.msc) |
Event logs | Built in | Connect to another computer |
Certificates (certlm.msc) |
Machine cert store | Built in | Connect to remote machine store |
Two things must be true for the MMC consoles to reach a Core box: the relevant firewall rules must be enabled on the server (many are governed by rule groups like “Remote Event Log Management,” “Remote Service Management,” “Remote Volume Management”), and you need name resolution + Kerberos (connect by FQDN, not IP). Enable the rule groups you need on the node:
# On the Core node — allow the MMC consoles' underlying RPC/WMI traffic
Enable-NetFirewallRule -DisplayGroup "Remote Event Log Management"
Enable-NetFirewallRule -DisplayGroup "Remote Service Management"
Enable-NetFirewallRule -DisplayGroup "Remote Volume Management"
Enable-NetFirewallRule -DisplayGroup "Windows Management Instrumentation (WMI)"
Windows Admin Center: gateway, connections, extensions, and roles
Windows Admin Center is the browser-based, GUI-equivalent experience over the same WinRM plumbing — no agent on the managed nodes. The right topology for a team is gateway mode: install WAC as a Windows service on a dedicated management server (which can itself be Server Core), not in desktop mode on someone’s laptop. A gateway is centrally patched, its access is auditable, and everyone points a browser at one URL.
As of 2024+, Windows Admin Center ships as a modern installer (
WindowsAdminCenter.exe/MSI) that supersedes the older “Project Honolulu” MSI. The gateway install, the port binding, and the certificate story are the same in spirit; use the current installer and follow its switches. The concepts below — gateway, connections, extensions, JEA target — are stable.
Install silently, generating a self-signed cert only for the first stand-up. In production, supply your own CA-issued certificate:
# Gateway install on a dedicated management host, port 443 (older MSI switches shown)
msiexec /i WindowsAdminCenter.msi /qn /L*v wac-install.log `
SME_PORT=443 SSL_CERTIFICATE_OPTION=generate
After install, the gateway listens on 443. Replace the generated certificate with a CA-issued one bound to the gateway’s FQDN before anyone uses it — a self-signed gateway cert trains operators to click through TLS warnings, which is exactly the habit you do not want on a management plane.
Add nodes from PowerShell so the inventory is repeatable rather than hand-entered. The WAC ConnectionTools module imports a list of connections:
Import-Module "$env:ProgramFiles\WindowsAdminCenter\PowerShell\Modules\ConnectionTools"
$nodes = "srv-core-01.corp.example.com","srv-core-02.corp.example.com"
$nodes | ForEach-Object {
"$_|msft.sme.connection-type.server|$_"
} | Set-Content .\wac-connections.txt
Import-Connection -GatewayEndpoint "https://wac.corp.example.com" `
-FileName .\wac-connections.txt
WAC’s built-in tools cover essentially every headless day-two task through the browser. The most-used ones and what they replace:
| WAC tool | What it does | GUI it replaces | Underlying mechanism |
|---|---|---|---|
| Overview | CPU/RAM/uptime, restart/shutdown, edit env | Server Manager dashboard | WinRM/CIM |
| Roles & features | Add/remove roles | Add Roles wizard | Install-WindowsFeature |
| Events | Browse/filter event logs | Event Viewer | Get-WinEvent |
| Files & file sharing | Browse FS, manage SMB shares | Explorer + fsmgmt.msc |
CIM/WinRM |
| Storage / Volumes / Disks | Init/partition/format, pools | Disk Management | Storage module |
| Certificates | Machine cert store | certlm.msc |
CIM |
| Services | Start/stop/config services | services.msc |
Get/Set-Service |
| Performance Monitor | Live/recorded counters | perfmon.exe |
Get-Counter / PDH |
| Registry | View/edit registry | regedit |
CIM |
| PowerShell | In-browser remote console | — | PowerShell remoting |
| Remote Desktop | In-browser RDP (to the console) | mstsc |
RDP |
| Updates | View/install Windows Updates | Windows Update | WU API |
| Virtual machines / switches | Manage Hyper-V | Hyper-V Manager | Hyper-V WMI |
Extensions add capability without re-installing anything. WAC has an extension feed; you add extensions for Cluster management, the SDN/Networking extension, vendor hardware (Dell/HPE/Lenovo) health, and Azure hybrid services (Backup, Monitor, Update Manager, Security). Manage them under Settings → Extensions, or from the gateway host:
# List and install extensions from the configured feed (module name varies by build)
Get-Command -Module *Extension* -ErrorAction SilentlyContinue
# In the UI: Settings > Extensions > choose an extension > Install (gateway restarts the tool)
The topology and its rules, side by side, so you deploy the right shape:
| WAC install mode | Runs as | Who reaches it | Use for | Certificate |
|---|---|---|---|---|
| Gateway (service) | Windows service on a server | A whole team via browser | Production; central mgmt of a fleet | CA-issued, gateway FQDN |
| Desktop | App on Win10/11 | Just that user | Ad-hoc, a single admin’s laptop | Self-signed local |
| Failover-cluster (HA) | Clustered gateway role | A team, highly available | Large estates needing HA mgmt plane | CA-issued |
WAC connects to nodes over WinRM as the signed-in user (single sign-on with Kerberos) when the gateway and nodes are in the same domain. Managing non-domain or cross-domain nodes through the gateway needs CredSSP or a configured resource-based constrained delegation (RBCD) path — the double-hop problem, covered in troubleshooting below. Register the gateway with Azure Arc to front WAC with Entra ID auth and conditional access, and operate the fleet from the Azure portal without opening an inbound management port to the internet.
The missing-GUI workflows: events, performance, storage, users, services, certs
You lost Event Viewer, Performance Monitor, and Disk Management as windows — but every one of them is now an object pipeline, which is strictly more powerful for a fleet. This section is the reference for the tasks that specifically trip people up because the GUI is gone.
Event logs — Get-WinEvent
Query the modern channels with a hashtable filter so you pull only what matters across many nodes at once — the equivalent of setting a custom view in Event Viewer, but scriptable and fannable:
Invoke-Command -ComputerName srv-core-01,srv-core-02 -UseSSL {
Get-WinEvent -FilterHashtable @{
LogName = 'System'
Level = 1,2 # 1 = Critical, 2 = Error
StartTime = (Get-Date).AddHours(-24)
} -ErrorAction SilentlyContinue
} | Select-Object PSComputerName, TimeCreated, Id, ProviderName, Message
The Get-WinEvent filter fields you will actually use:
| Filter key | Meaning | Example value |
|---|---|---|
LogName |
Channel to query | System, Application, Security, Microsoft-Windows-... |
Level |
Severity | 1=Critical, 2=Error, 3=Warning, 4=Info |
Id |
Event ID(s) | 4624, 7036 |
ProviderName |
Source | Service Control Manager |
StartTime / EndTime |
Time window | (Get-Date).AddHours(-1) |
Data |
Event data field match | provider-specific |
Performance — Get-Counter
Pull live counters without opening perfmon. Sample CPU, memory, and disk queue against a node — the same counters, delivered as objects you can threshold and alert on:
Get-Counter -ComputerName srv-core-01.corp.example.com `
-Counter '\Processor(_Total)\% Processor Time',
'\Memory\Available MBytes',
'\PhysicalDisk(_Total)\Avg. Disk Queue Length' `
-SampleInterval 5 -MaxSamples 3
Storage — the Storage module
The Storage module replaces Disk Management wholesale. Initialize a new disk, partition it, and format it end to end in one pipeline:
Invoke-Command -ComputerName srv-core-02.corp.example.com -UseSSL {
Get-Disk | Where-Object PartitionStyle -eq 'RAW' |
Initialize-Disk -PartitionStyle GPT -PassThru |
New-Partition -AssignDriveLetter -UseMaximumSize |
Format-Volume -FileSystem NTFS -NewFileSystemLabel 'Data' -Confirm:$false
}
The Disk-Management-equivalent cmdlets, task by task:
| Disk Management action | Cmdlet | Note |
|---|---|---|
| See disks/volumes | Get-Disk, Get-Volume, Get-Partition |
Objects, filterable |
| Initialize a new disk | Initialize-Disk -PartitionStyle GPT |
GPT over MBR for modern disks |
| Create a partition | New-Partition -UseMaximumSize -AssignDriveLetter |
Or -DriveLetter |
| Format a volume | Format-Volume -FileSystem NTFS |
-FileSystem ReFS for S2D/Hyper-V |
| Extend a volume | Resize-Partition -Size ... |
Grow into free space |
| Bring a disk online / RW | Set-Disk -IsOffline $false -IsReadOnly $false |
Common after attaching |
| Create a storage pool | New-StoragePool |
Storage Spaces / S2D territory |
Users, services, and certificates
Local users (rare on domain-joined boxes, but real for break-glass and workgroup edge), services, and certificates are all cmdlet-driven:
# Local users/groups (workgroup / break-glass)
Get-LocalUser; New-LocalUser -Name "svc-break" -NoPassword:$false
Add-LocalGroupMember -Group "Administrators" -Member "CORP\ServerAdmins"
# Services
Get-Service W3SVC | Select-Object Status, StartType
Set-Service -Name Spooler -StartupType Disabled # e.g. hardening a print-free server
Restart-Service -Name W3SVC
# Certificates — the Cert: PSDrive is your certlm.msc
Get-ChildItem Cert:\LocalMachine\My |
Select-Object Subject, NotAfter, Thumbprint, EnhancedKeyUsageList
Application compatibility: Features on Demand and the App Compatibility FoD
The most common objection to Server Core — “our app won’t install, it needs the GUI” — usually has nothing to do with the desktop and everything to do with a handful of GUI DLLs and diagnostic tools the installer or the app links against. Rather than concede to Desktop Experience, add the Server Core App Compatibility Feature on Demand (ServerCore.AppCompatibility). It restores those specific components — mmc.exe, eventvwr.msc, perfmon.exe, resmon.exe, dsregcmd, parts of the shell, and a minimal Explorer — on top of Core, without turning the box into a full Desktop Experience install. You keep the lean base and unblock the app.
Check availability, then add it (online if the box has access to Windows Update, or from an offline FoD ISO with -Source):
# Is the App Compatibility FoD present or available?
Get-WindowsCapability -Online -Name "ServerCore.AppCompatibility*"
# Add it online...
Add-WindowsCapability -Online -Name "ServerCore.AppCompatibility~~~~0.0.1.0"
# ...or from a mounted FoD ISO on an air-gapped box, then reboot
Add-WindowsCapability -Online -Name "ServerCore.AppCompatibility~~~~0.0.1.0" `
-Source "D:\" -LimitAccess
What the App Compatibility FoD does and does not give you:
| Aspect | With App Compatibility FoD | Full Desktop Experience |
|---|---|---|
mmc.exe + common snap-ins locally |
Yes | Yes |
| Event Viewer / Perfmon / Resmon locally | Yes | Yes |
| Minimal File Explorer | Yes (limited) | Yes (full shell) |
| Microsoft Edge / browser | No | Yes |
| Start menu / full desktop shell | No | Yes |
| Media / Paint / Windows apps | No | Yes |
| Footprint added | Modest (a few components) | Large (full GUI stack) |
| Patch surface added | Small | Full GUI servicing |
| Reversible | Yes (Remove-WindowsCapability) |
No (one-way install choice) |
Decide app-compat in this order, escalating only when forced:
| Symptom | First try | Then | Last resort |
|---|---|---|---|
| Installer errors on a missing GUI DLL | Add App Compatibility FoD | Vendor’s silent/headless installer switches | Desktop Experience |
App needs mmc/perfmon to run |
Add App Compatibility FoD | Manage the app remotely from a workstation | Desktop Experience |
| App needs .NET 3.5 | Add-WindowsCapability/Install-WindowsFeature NET-Framework-Core with -Source |
— | — |
| App needs a specific legacy component | Get-WindowsCapability -Online and add it |
Vendor support case | Desktop Experience |
The App Compatibility FoD is the reason “we can’t use Core” is almost never true. Reach for it before you concede the GUI. Reserve Desktop Experience for genuine Remote Desktop Session Hosts and the rare vendor that contractually refuses to support anything but the full desktop — and, as always, push that vendor.
Architecture at a glance
Picture the management plane as three concentric surfaces around a headless node, all riding the same WinRM transport. At the centre sits a Server Core box: no explorer.exe, a single console for break-glass, WinRM listening on 5986 (HTTPS) with the HTTP listener removed, Basic auth and unencrypted traffic disabled, and — if delegated ops are needed — a JEA endpoint (ServiceOps) registered as a constrained, transcribed session. That node is cattle: provisioned by an unattend.xml, named and domain-joined before a human ever touches it.
Around it, three ways in. First, an admin workstation running native PowerShell remoting — Enter-PSSession and Invoke-Command -UseSSL fanning commands across many nodes at once; this is the automation surface and the one your runbooks call. Second, the same workstation (or a management server) running RSAT — Server Manager, ADUC, DNS Manager, Hyper-V Manager, Event Viewer, Computer Management — each classic MMC console pointed at the remote node over Kerberos, giving you the familiar GUI without a single GUI byte on the server. Third, a Windows Admin Center gateway: a dedicated (often Core) server running WAC as a service on 443 with a CA-issued certificate, its connection inventory imported from PowerShell, its capability extended by the Cluster/SDN/vendor/Azure extensions, reaching each node over WinRM as the signed-in user via Kerberos SSO — and a whole team pointing a browser at one URL.
The flows all converge on WinRM: PowerShell → 5986; WAC gateway → 5986; RSAT/MMC → the role’s RPC/WMI ports plus WinRM for some tools. Kerberos authenticates and encrypts inside the domain; HTTPS carries the non-domain and cross-forest hops; the double-hop cases (WAC or a front-end node reaching a second server on the user’s behalf) are solved with resource-based constrained delegation scoped to named front-ends, never unconstrained delegation or blanket CredSSP. Follow any path inward and you land on the same lean node — the desktop is gone, but every management capability it used to host now lives one hop away, centralised and auditable.
Real-world scenario
Meridian Freight, a logistics company, ran a 200-node estate — Hyper-V hosts, file servers, domain controllers, and IIS middleware — and standardised on Server Core to cut their patch surface and monthly reboot count. The build went clean: golden images with unattend.xml, WinRM HTTPS listeners rolled out by a startup script, WAC in gateway mode on a dedicated Core box, and RSAT on the ops team’s Windows 11 workstations. For three weeks it was uneventful — fewer patches, faster maintenance windows, exactly the pitch.
Then the operations team started hitting WinRM 0x80090322 errors when WAC reached file servers that, in turn, needed to read from a back-end SMB cluster on the user’s behalf. Single-hop commands worked perfectly; anything touching a second server failed. Classic Kerberos double-hop: the credential WAC delegated to node A could not be reused by node A to authenticate to node B. The on-call engineer’s first instinct — enable CredSSP everywhere, or turn on unconstrained delegation — got flagged in review, because unconstrained delegation lets a compromised intermediary impersonate the operator against any service in the forest, and CredSSP caches credentials on the intermediary. On a fleet this size, that was an unacceptable blast radius.
The correct, scoped fix was resource-based constrained delegation (RBCD), configured on the resource (the back-end node) to trust only the specific front-end nodes:
# On a DC / mgmt host: let the front-end file servers delegate to the back-end cluster node
$frontends = Get-ADComputer -Filter 'Name -like "SRV-FILE-0*"'
Set-ADComputer -Identity "SRV-FILE-BACK01" `
-PrincipalsAllowedToDelegateToAccount $frontends
Because RBCD is administered on the resource owner’s side, the team that owned the back-end cluster controlled exactly which front-ends could impersonate users against it — no domain-admin change to the front-ends, no forest-wide trust, no credentials cached anywhere. After clearing the stale ticket cache (klist purge) on the front-end nodes, the WAC storage and file-share tools worked through the double hop, and the unconstrained-delegation security finding never had to be opened.
A second, quieter win came from the app-compat side. A legacy monitoring agent the company was contractually stuck with “required the GUI” and nearly forced three of the file servers onto Desktop Experience. Instead, the team added the App Compatibility FoD (ServerCore.AppCompatibility) to those three boxes — the agent’s installer found the mmc and perf DLLs it wanted, installed cleanly, and the boxes stayed on Core. The lesson the team wrote on the wall: when a headless, gateway-fronted fleet hits a wall, the answer is almost always a scoped, native mechanism — RBCD for double-hop, a Feature on Demand for app-compat — not the sledgehammer of unconstrained delegation or a full desktop install.
Advantages and disadvantages
The headless-with-remote-management model both demands discipline and rewards it. Weigh it honestly:
| Advantages | Disadvantages |
|---|---|
| Smaller attack surface — fewer binaries, services, and listeners to compromise or patch | Steeper learning curve for teams used to wizards; the black screen intimidates |
| Fewer patches and reboots — absent components generate no servicing | The Core install choice is one-way; a wrong call means a rebuild, not a toggle |
| Smaller disk/memory footprint — matters at fleet and edge scale | A stubborn vendor app may still need FoD or (rarely) Desktop Experience |
| Forces remote, declarative admin — the good habit, removing hand-edited prod boxes | Every remote surface (WinRM, RSAT firewall rules, WAC cert) is a thing to configure and secure |
| Three complementary surfaces (PowerShell, RSAT, WAC) — pick the right tool per task | Double-hop and Kerberos-SPN issues appear that a single local session never surfaces |
| WAC gives a whole team a GUI-equivalent over one auditable gateway, no node agents | WAC gateway is another server to patch, cert-manage, and keep highly available |
| JEA on Core delegates day-two work with no standing admin, fully transcribed | JEA role/session files are more upfront design than “just add them to Administrators” |
The model is right for essentially every standard Windows Server role — DCs, DNS/DHCP, file, Hyper-V, IIS, middleware — where you want a lean, patch-light, remotely-managed box. It bites hardest on teams that never invest the week to learn the remote surfaces (who then default to Desktop Experience and lose all the benefit), and on estates with a genuine hard-GUI-installer dependency that FoD cannot satisfy. Every disadvantage is manageable — but only if you know it exists before you are staring at a black screen at 2 a.m.
Hands-on lab
Stand up a Server Core-style headless workflow end to end: enable and harden remoting, register a JEA endpoint, do a missing-GUI task (storage), and tear it all down. If you have a spare Server Core VM (2022/2025), run these against it from your workstation; if you only have one box, run the node-side steps locally and the client-side steps from the same PowerShell — the commands are identical. Everything here is free (uses features already in Windows Server).
Step 1 — Confirm the box and its install type. On the node’s console:
# 'Server Core' has no shell; confirm the SKU and that there's no Desktop Experience
Get-ComputerInfo -Property WindowsProductName, OsServerLevel, WindowsInstallationType
Expected: WindowsInstallationType = Server Core.
Step 2 — Enable remoting and verify WinRM. On the node:
Enable-PSRemoting -Force
Get-Service WinRM | Select-Object Status, StartType # Running / Automatic
Get-ChildItem WSMan:\localhost\Listener # note the HTTP (5985) listener
Step 3 — Stand up an HTTPS listener (self-signed for the lab). On the node:
$cert = New-SelfSignedCertificate -DnsName $env:COMPUTERNAME `
-CertStoreLocation Cert:\LocalMachine\My
New-Item -Path WSMan:\localhost\Listener -Transport HTTPS -Address * `
-HostName $env:COMPUTERNAME -CertificateThumbPrint $cert.Thumbprint -Force
New-NetFirewallRule -DisplayName "WinRM HTTPS lab (5986)" -Direction Inbound `
-Protocol TCP -LocalPort 5986 -Action Allow
Expected: a new listener with Transport=HTTPS, Port=5986. (In production you would use a CA-issued cert, not New-SelfSignedCertificate.)
Step 4 — Harden the WinRM service. On the node:
Set-Item WSMan:\localhost\Service\AllowUnencrypted $false
Set-Item WSMan:\localhost\Service\Auth\Basic $false
Get-Item WSMan:\localhost\Service\Auth\Basic # should read False
Step 5 — Register a JEA endpoint. On the node:
$m = "$env:ProgramFiles\WindowsPowerShell\Modules\LabOps\RoleCapabilities"
New-Item $m -ItemType Directory -Force | Out-Null
New-PSRoleCapabilityFile -Path "$m\SvcOp.psrc" `
-VisibleCmdlets 'Get-Service','Restart-Service'
New-PSSessionConfigurationFile -Path .\LabOps.pssc `
-SessionType RestrictedRemoteServer -RunAsVirtualAccount `
-RoleDefinitions @{ "$env:COMPUTERNAME\Administrators" = @{ RoleCapabilities = 'SvcOp' } }
Register-PSSessionConfiguration -Name "LabOps" -Path .\LabOps.pssc -Force
Expected: Get-PSSessionConfiguration -Name LabOps returns the endpoint with RunAsVirtualAccount = True.
Step 6 — Prove the constraint. From a client (or the same box):
Invoke-Command -ComputerName $env:COMPUTERNAME -ConfigurationName LabOps { Get-Command } |
Select-Object -ExpandProperty Name
# Expect ONLY Get-Service, Restart-Service (+ a few default proxy cmdlets) — nothing else
Step 7 — A missing-GUI task: read errors as objects. From a client:
Get-WinEvent -ComputerName $env:COMPUTERNAME -FilterHashtable @{
LogName='System'; Level=1,2; StartTime=(Get-Date).AddDays(-1)
} -ErrorAction SilentlyContinue | Select-Object TimeCreated, Id, ProviderName -First 10
Step 8 — (Optional) Add the App Compatibility FoD. If the box has Windows Update access:
Get-WindowsCapability -Online -Name "ServerCore.AppCompatibility*" |
Select-Object Name, State
# Add-WindowsCapability -Online -Name "ServerCore.AppCompatibility~~~~0.0.1.0" # reboots
Step 9 — Teardown. Remove what the lab created:
Unregister-PSSessionConfiguration -Name "LabOps" -Force
Get-ChildItem WSMan:\localhost\Listener |
Where-Object { $_.Keys -contains "Transport=HTTPS" } | Remove-Item -Recurse
Remove-NetFirewallRule -DisplayName "WinRM HTTPS lab (5986)"
Get-ChildItem Cert:\LocalMachine\My | Where-Object Subject -match $env:COMPUTERNAME |
Remove-Item
Remove-Item "$env:ProgramFiles\WindowsPowerShell\Modules\LabOps" -Recurse -Force
You have enabled and hardened remoting, stood up and proven a JEA constraint, and done a real day-two task headless — the full loop, with a clean teardown.
Common mistakes & troubleshooting
Troubleshooting a box you cannot see is the skill that separates comfort from panic. The field guide — symptom, the likely cause, how to confirm it, and the fix — is the centrepiece here:
| # | Symptom | Likely cause | Confirm with | Fix |
|---|---|---|---|---|
| 1 | Test-WSMan -UseSSL fails, HTTP works |
Cert FQDN/SAN mismatch, expired, or untrusted CA | Get-ChildItem Cert:\LocalMachine\My | fl Subject,NotAfter,EnhancedKeyUsageList |
Reissue with correct DNS name + Server Auth EKU; trust the CA on the client |
| 2 | Connecting to remote server failed ... Access is denied |
Kerberos can’t resolve the SPN; connecting by IP not FQDN | setspn -L <computer>; try FQDN |
Connect by FQDN; ensure HOST/<fqdn> SPN exists |
| 3 | WinRM 0x80090322 on multi-server WAC/tools |
Kerberos double-hop | Single-hop works, second-hop fails | Configure RBCD on the resource, scoped to named front-ends; klist purge |
| 4 | WAC node shows “not accessible” | Firewall 5986 closed, or listener on wrong/expired cert | Test-NetConnection <node> -Port 5986; check listener thumbprint |
Open 5986; rebind listener to a valid cert |
| 5 | Workgroup/non-domain node unreachable | No Kerberos; TrustedHosts not set |
Get-Item WSMan:\localhost\Client\TrustedHosts |
Add the node’s FQDN to TrustedHosts (never *) + use HTTPS/cert auth |
| 6 | RDP to Core shows a black screen with no window | Console session, explorer.exe absent — this is normal |
You see a cmd/PowerShell window only |
Not a fault; type powershell, or manage remotely |
| 7 | MMC console (Event Viewer, etc.) can’t connect to the node | Remote-management firewall rule group disabled | Get-NetFirewallRule -DisplayGroup "Remote Event Log Management" |
Enable-NetFirewallRule -DisplayGroup "Remote Event Log Management" (and peers) |
| 8 | App installer aborts: “requires the graphical shell” | Missing GUI DLLs, not the whole desktop | Installer log names an mmc/shell DLL |
Add App Compatibility FoD (ServerCore.AppCompatibility), reboot, retry |
| 9 | Disk not visible / read-only after attach | Disk offline or read-only | Get-Disk | ft Number,IsOffline,IsReadOnly |
Set-Disk -Number N -IsOffline $false -IsReadOnly $false |
| 10 | Driver won’t load after pnputil /add-driver |
Unsigned driver or wrong architecture | pnputil /enum-drivers; check signature |
Use a signed x64 driver; verify the INF architecture matches |
| 11 | A service is hung; can’t open services.msc locally |
No local GUI (expected) | Get-Service X; sc.exe queryex X for PID |
Restart-Service X, or Stop-Process -Id <PID> -Force then start |
| 12 | Disk filling; can’t browse in Explorer | No shell to click through folders | Get-PSDrive C; Get-ChildItem C:\ -Recurse ... | Sort Length -Desc |
Delete/relocate the largest offenders; clean CBS/temp; Cleanmgr isn’t on Core |
| 13 | Can’t confirm what changed / who restarted the box | No local event GUI habit | Get-WinEvent -LogName System | Where Id -in 6005,6006,1074,41 |
Read shutdown/boot/Kernel-Power events for the story |
| 14 | Time is skewed; Kerberos starts failing | W32Time not synced to the domain hierarchy | w32tm /query /status; w32tm /monitor |
Re-sync per Authoritative time sync on Windows and Linux |
Two quick diagnostic reflexes worth committing to muscle memory. When Kerberos is suspect, the fastest signal is the ticket cache — a stale or missing service ticket explains most “access is denied” errors after any delegation or SPN change:
klist purge # clear stale tickets
klist get HOST/srv-core-01.corp.example.com # force a fresh ticket for the SPN
When anything remote fails, confirm the transport before you theorise — is the port even open, and does the listener answer?
Test-NetConnection srv-core-01.corp.example.com -Port 5986 # is 5986 reachable?
Test-WSMan -ComputerName srv-core-01.corp.example.com -UseSSL # does WinRM answer over TLS?
If
Test-WSMan -UseSSLfails but the plain (HTTP) test passes, the certificate is the problem about 90% of the time — wrong FQDN in the subject/SAN, expired, or an issuing CA the client does not trust. Check the cert and the client’s trust chain before touching listeners, firewall, or auth.
Best practices
- Default to Server Core; treat Desktop Experience as the exception. Reserve the full GUI for Remote Desktop Session Hosts and genuine hard-GUI-installer apps that FoD cannot satisfy.
- Provision as cattle, not pets. Bake identity, IP, and domain join into an
unattend.xml; inject secrets at deploy time;sysprep /generalizegolden images.sconfigis for break-glass only. - Standardise on WinRM HTTPS (5986); remove the HTTP listener after you have proven HTTPS fleet-wide. Keep
AllowUnencryptedand Basic auth off. - Bind listeners and the WAC gateway to CA-issued certificates, never self-signed in production — a self-signed cert trains operators to click through TLS warnings.
- Install WAC in gateway mode on a dedicated (Core) host, patch and cert-manage it like any production server, and consider a clustered gateway for HA on large estates.
- Import WAC connections from PowerShell, not by hand — the inventory should be repeatable and reviewable, not tribal knowledge.
- Delegate with JEA, never with local Administrators. Constrained, virtual-account, transcribed endpoints are the correct interface for day-two ops on a headless box.
- Solve double-hop with RBCD scoped to named principals. Never reach for unconstrained delegation or blanket CredSSP.
- Scope
TrustedHoststo explicit FQDNs; never*. Prefer bringing a node into the domain or using certificate auth over relying onTrustedHostsat all. - Keep App Compatibility FoD in your toolkit — it unblocks most “needs the GUI” apps without conceding Desktop Experience, and it is reversible.
- Make configuration declarative. Enforce state with DSC/Ansible (Configuration Management for Windows Server with PowerShell DSC and Ansible) so a hand-run
pnputilor feature install never silently drifts a node. - Wire event/perf/storage queries into monitoring runbooks, not ad-hoc console sessions — the object pipeline exists precisely so you can automate it.
Security notes
Server Core is a security posture, not just an install option — lean into it. Every component you did not install is one you do not have to patch, monitor, or defend; keep it that way by adding FoD payloads deliberately and removing feature payloads you do not need (Uninstall-WindowsFeature -Remove).
The management plane is now your highest-value surface, so harden it accordingly. WinRM must be HTTPS-only with Basic auth and unencrypted traffic disabled; the HTTP listener removed; and TrustedHosts empty or narrowly enumerated. Delegation is where headless fleets get compromised — never use unconstrained delegation, avoid CredSSP (it caches credentials on the intermediary), and solve every double-hop with resource-based constrained delegation scoped to named front-ends so a single compromised intermediary cannot impersonate operators forest-wide.
Nobody should hold standing local admin. Deliver day-two operations through JEA endpoints running as virtual accounts, with every session transcribed to a write-protected location and shipped to your SIEM as a privileged-access audit trail. Pair this with gMSA and Windows LAPS (Eliminating Static Service Credentials with gMSA and Windows LAPS) so service and local-admin credentials are rotated automatically and never static. Front the Windows Admin Center gateway with a CA-issued certificate and, ideally, register it with Azure Arc so Entra ID authentication and conditional access sit in front of the whole fleet — letting you operate headless boxes without opening an inbound management port to the internet at all. Finally, apply the same hardening baseline you would to any Windows Server (Hardening Windows Server and Building a Reliable WSUS Patch Pipeline); Core gives you a smaller surface to harden, not a pass on hardening.
Cost & sizing
Server Core has no separate license — it is the same Windows Server license as Desktop Experience, chosen at install time — so the “cost” story is entirely about density and operations, not SKUs. The savings are real but indirect:
| Cost driver | Server Core effect | Rough magnitude |
|---|---|---|
| Windows Server license | Identical to Desktop Experience | No difference |
| Disk per host | Smaller base image | Several GB saved per host |
| Memory per host | Smaller baseline working set | Hundreds of MB freed per host; more VMs per hypervisor |
| Patch/reboot labour | Fewer components → fewer updates/reboots | Recovered maintenance-window hours across a fleet |
| WAC gateway | One (or two, for HA) management servers | A B-series-equivalent VM or two; negligible vs the estate |
| RSAT | Free (built into Windows 10/11 as FoD) | ₹0 |
| Windows Admin Center | Free (no per-node license) | ₹0 |
Sizing guidance: the WAC gateway is light — a modest server (2–4 vCPU, 4–8 GB RAM) handles a few hundred nodes comfortably; scale up only for very large estates or when you cluster it for HA. Do not over-provision Core nodes on the assumption they need “GUI headroom” — they need less than the equivalent Desktop Experience box for the same role, so right-size the role’s actual workload and pocket the difference. Across a large estate, the aggregate disk saved, the extra VMs you fit per hypervisor host, and the maintenance-window hours you recover from fewer reboots are where Core pays for the week you spent learning it. In INR terms on a cloud estate, packing even 10–15% more Core VMs onto the same hypervisor hosts, or shrinking hosts, moves the monthly bill by a meaningful margin at scale — while the tooling to manage them all (RSAT, WAC) costs nothing.
Interview & exam questions
Q1. Why is Server Core more secure than Desktop Experience? Fewer installed components means a smaller attack surface — no shell, no Edge, no media, fewer services and listeners — so fewer CVEs apply and there is less to compromise. It also patches and reboots less because absent components generate no servicing. Maps to security-hardening and Windows Server admin objectives.
Q2. Can you convert a Server Core install to Desktop Experience after deployment? No. The install option is chosen at setup and is a one-way door — the in-place feature switch was removed after Server 2012 R2. Changing your mind means a rebuild, so choose deliberately.
Q3. What is the difference between the WinRM HTTP (5985) and HTTPS (5986) listeners, and is HTTP plaintext in a domain? 5985 is HTTP, 5986 is HTTPS. In a domain, Kerberos already encrypts the payload over the HTTP listener, so it is not plaintext — but you standardise on 5986 for non-domain/cross-forest hops and uniform TLS negotiation, then remove the HTTP listener.
Q4. What does Install-WindowsFeature -IncludeManagementTools add, and why does it matter on Core?
It adds the role’s remote-administration cmdlet modules and RSAT snap-in support — not the desktop GUI. That distinction is what keeps Core lean: you get the management surface without explorer.exe.
Q5. What is JEA and why is it especially valuable on Server Core? Just Enough Administration exposes a constrained PowerShell endpoint where a user runs as a temporary virtual account with only whitelisted cmdlets, fully transcribed. On Core it is the interface for delegated ops — there is no local GUI to fall back on — so operators never need standing local admin.
Q6. An operator gets WinRM 0x80090322 only when a WAC tool touches a second server. What is happening and how do you fix it correctly?
It is the Kerberos double-hop: the credential delegated to the first node can’t be reused to reach the second. The correct fix is resource-based constrained delegation on the resource, scoped to the named front-end nodes — not unconstrained delegation and not blanket CredSSP.
Q7. A vendor app won’t install because it “needs the GUI.” What do you try before Desktop Experience?
Add the Server Core App Compatibility Feature on Demand (ServerCore.AppCompatibility), which restores the specific GUI DLLs and tools (mmc, perfmon, Event Viewer, limited Explorer) without a full desktop. Reserve Desktop Experience for session hosts and vendors that contractually refuse Core.
Q8. How do you install a NIC driver on a running Server Core box with no Device Manager?
pnputil /add-driver <path>\*.inf /install on the live OS, then pnputil /enum-drivers to confirm. Add-WindowsDriver (DISM) is for offline images, not the running host.
Q9. Windows Admin Center — what is gateway mode and why prefer it over desktop mode in production? Gateway mode runs WAC as a Windows service on a dedicated (often Core) server that a whole team reaches by browser; it is centrally patched, auditable, and its connections are managed centrally. Desktop mode is a single admin’s laptop app — fine for ad-hoc use, wrong for a fleet.
Q10. Test-WSMan -UseSSL fails but the plain test succeeds. What’s the most likely cause and first check?
The certificate — wrong FQDN/SAN, expired, or a CA the client doesn’t trust — about 90% of the time. Check the cert’s subject/SAN, expiry, and Server Authentication EKU, and the client’s trust chain, before touching listeners or firewall.
Q11. Why should TrustedHosts never be set to *?
It tells WinRM to trust the identity of any host you connect to, defeating mutual authentication and inviting man-in-the-middle. Enumerate specific FQDNs, or better, keep nodes domain-joined (Kerberos) or use certificate auth.
Q12. How do you read the last shutdown/restart cause on a headless box with no Event Viewer window?
Get-WinEvent -LogName System | Where-Object Id -in 6005,6006,1074,41 — 6005/6006 are event-log start/stop (boot/clean-shutdown), 1074 is an initiated shutdown/restart with the reason and user, and 41 (Kernel-Power) indicates an unexpected power loss.
Quick check
- Can you convert a running Server Core box to Desktop Experience? What does that imply for how you choose the install option?
- Which WinRM port and listener should a hardened fleet standardise on, and what two settings do you disable on the WinRM service?
- What does the App Compatibility FoD (
ServerCore.AppCompatibility) give you, and what does it deliberately not give you? - You get
0x80090322only on multi-server WAC tools. What is the cause, and what is the correct fix? - Name the three remote-management surfaces for a headless box and one task each is best at.
Answers
- No — it is a one-way choice made at setup. So you must pick Core deliberately for the workload; a wrong call means a rebuild, not a toggle.
- HTTPS on 5986 (remove the HTTP 5985 listener after proving HTTPS). Disable
AllowUnencryptedand Basic auth on the WinRM service. - It restores specific GUI DLLs and diagnostic tools (
mmc,perfmon,resmon, Event Viewer, a limited Explorer) so GUI-dependent apps install and run. It does not add Edge, the Start menu / full shell, or the media/Windows apps — it is not Desktop Experience. - The Kerberos double-hop — the credential delegated to the first node can’t reach a second. Fix with resource-based constrained delegation (RBCD) on the resource, scoped to the named front-end nodes; then
klist purge. Never unconstrained delegation or blanket CredSSP. - Native PowerShell remoting (best for automation and fleet fan-out), RSAT/MMC (best for familiar one-box GUI tasks like DNS Manager or Hyper-V Manager), and Windows Admin Center (best for a team’s GUI-equivalent dashboards over one auditable gateway).
Glossary
- Server Core — A Windows Server installation option with no desktop shell, GUI consoles, or browser; managed remotely. Chosen at setup and not convertible afterward.
- Desktop Experience — The full-GUI Windows Server install option, with
explorer.exe, MMC snap-ins, Edge, and media components. sconfig— A text-menu utility on Server Core that wraps common configuration commands (name, domain join, network, remote management), for first-boot bring-up and break-glass.- WinRM (Windows Remote Management) — Microsoft’s WS-Management implementation; the transport for PowerShell remoting and Windows Admin Center. Listens on 5985 (HTTP) and 5986 (HTTPS).
- PowerShell remoting — Running commands on a remote machine over WinRM via
Enter-PSSession(interactive) orInvoke-Command(fan-out/scripted). - RSAT (Remote Server Administration Tools) — The package of MMC consoles and PowerShell modules that lets a Windows client or management server administer remote servers.
- MMC (Microsoft Management Console) — The host for classic admin snap-ins (ADUC, DNS Manager, Event Viewer, Computer Management), run on a workstation and pointed at remote servers.
- Windows Admin Center (WAC) — A browser-based management console served by a gateway server, reaching nodes over WinRM with no node agent.
- Gateway mode — Installing WAC as a Windows service on a dedicated server for team-wide, centrally-managed, auditable access.
- JEA (Just Enough Administration) — A constrained PowerShell endpoint where a user runs as a temporary virtual account with only whitelisted cmdlets, fully transcribed.
- Virtual account — The ephemeral, machine-local admin identity a JEA session runs as, so operators never hold standing credentials.
- Features on Demand (FoD) — Optional OS payloads added on demand (e.g. the App Compatibility FoD, RSAT capabilities, .NET 3.5).
- App Compatibility FoD (
ServerCore.AppCompatibility) — A Feature on Demand that restores specific GUI DLLs and tools to Server Core so GUI-dependent apps run, without installing Desktop Experience. pnputil— The command-line driver utility for the live OS: add, enumerate, and remove driver packages without Device Manager.- Double-hop / RBCD — The Kerberos limitation where a delegated credential can’t reach a second server; solved by resource-based constrained delegation scoped to named front-ends.
TrustedHosts— The WinRM client list of hosts to trust without Kerberos; must be explicit FQDNs, never*.- Unattend.xml — An answer file that drives hands-off Windows setup (naming, network, domain join), applied at install or via
sysprep.
Next steps
- Make the configuration you set here declarative and enforced with Configuration Management for Windows Server with PowerShell DSC and Ansible.
- Harden and patch the leaner fleet with Hardening Windows Server and Building a Reliable WSUS Patch Pipeline.
- Eliminate standing credentials on these boxes with Eliminating Static Service Credentials with gMSA and Windows LAPS.
- If your Core boxes are domain controllers, design the forest they live in with Building an AD DS Forest the Right Way: Deployment, FSMO, and a Tiered Admin Model.
- Operate the fleet from the cloud — front WAC with Entra ID and manage without inbound ports — via Onboarding Servers to Azure Arc: Connected Machine Agent, Service Principals & Bulk Enrollment.