Servers Automation

Operating Server Core at Scale with Windows Admin Center and PowerShell Remoting

Server Core is the right default for almost every Windows Server role that does not need an interactive desktop. You ship roughly half the binaries, take a fraction of the monthly patches, and remove an entire class of GUI-driven misconfiguration. The catch is operational: once you remove explorer.exe, the muscle memory of clicking through Server Manager is gone, and a team that has never administered a headless box treats every task as a crisis. It is not. This walkthrough is the model I run at scale - provision with sconfig and an unattend file, lock down WinRM over HTTPS, front the fleet with a hardened Windows Admin Center (WAC) gateway, and delegate day-two work through Just Enough Administration (JEA) so operators never hold full admin.

Everything here targets Windows Server 2019/2022/2025 joined to a domain. The Server Core deployment option is selected at install time and cannot be converted to Desktop Experience afterward - that one-way door is a feature, not a limitation.

1. Why Server Core: attack surface and patch footprint

The argument for Server Core is measurable, not aesthetic.

The trade is that local administration is a single PowerShell window. That is fine: at scale you administer servers remotely and declaratively anyway. Server Core just forces the good habit.

Pick Server Core for domain controllers, DNS/DHCP, file servers, Hyper-V hosts, IIS, and most middleware. Avoid it only for third-party software with a hard GUI-install dependency that the vendor refuses to support headless - and push that vendor.

2. Initial configuration with sconfig and unattended provisioning

On first boot, Server Core drops you into a console with sconfig available - a menu-driven wrapper over the same commands you would script, useful for one-off bring-up:

sconfig
 1) Domain/Workgroup
 2) Computer Name
 8) Network Settings
 6) Install Updates
 15) Exit to command line

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 specialize:

<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 (Key Vault, a CI variable group, or djoin /provision blobs for offline domain join). For golden images, run sysprep /oobe /generalize /unattend:unattend.xml so each clone gets a unique SID and SusClientId.

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:

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
Add-Computer -DomainName corp.example.com -Credential (Get-Credential) -Restart

3. Enabling and securing PowerShell remoting and WinRM over HTTPS

In a domain, Enable-PSRemoting is effectively already done - WinRM is running and the firewall rule is open for the domain profile. 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.

Kerberos already encrypts PSRemoting traffic on a domain HTTP listener, so HTTP-in-domain is not plaintext. You still want HTTPS for non-domain endpoints, for cross-forest/workgroup management, and so WAC and other tooling can use TLS uniformly. Standardize on 5986.

First, get a certificate with the server’s FQDN in the subject (or SAN) and Server Authentication EKU. With an enterprise CA, request it from the host itself:

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 falls back to it, and harden the WinRM service to reject Basic auth and unencrypted traffic:

# Remove the HTTP listener after confirming HTTPS connectivity
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

Verify the connection from your admin workstation. 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)

4. Installing the Windows Admin Center gateway and registering nodes

Windows Admin Center gives you a browser-based, GUI-equivalent experience over the same remoting plumbing - no agent on the managed nodes, just WinRM. The right topology is gateway mode: install WAC as a service on a dedicated management server (itself Server Core), not in desktop mode on a laptop.

Install silently from the MSI, generating a self-signed cert for the first stand-up. In production, supply your own cert thumbprint via SME_THUMBPRINT instead:

# Gateway install on a dedicated Server Core management host, port 443
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, exactly the habit you do not want.

Add nodes from PowerShell so the inventory is repeatable rather than hand-entered. The WAC module imports a list of FQDNs:

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 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. If you also manage non-domain or cross-domain nodes through the gateway, those hops need CredSSP or a configured resource-based constrained delegation path - covered in troubleshooting below.

5. Delegated administration with Just Enough Administration (JEA)

The mistake most teams make is granting operators full local admin “just to restart a service.” JEA fixes this: it exposes a constrained PowerShell endpoint where a user runs as a temporary virtual account with only the cmdlets you whitelist, and every command is transcribed. This is the highest-leverage control for headless fleets, because there is no GUI to fall back on - the JEA endpoint is the interface.

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-EventLog',
                  @{ 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 now connects to the constrained endpoint and can do exactly what the role allows and nothing else:

Enter-PSSession -ComputerName srv-core-01.corp.example.com -UseSSL `
  -ConfigurationName "ServiceOps" -Credential (Get-Credential)

Inside that session, Get-Command returns only the whitelisted cmdlets, whoami shows the virtual account, and Stop-Service -Name WinRM is rejected. WAC can also target a JEA endpoint, so the browser experience inherits the same guardrails.

6. Managing roles, features, and drivers without a GUI

Everything Server Manager’s 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

-IncludeManagementTools brings in the remote-administration cmdlet modules without dragging in the desktop GUI - that distinction is what keeps Core lean. 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
}

Drivers without Device Manager use pnputil, which is fully scriptable. Stage a driver package and confirm it landed:

# Add a driver package, then list third-party drivers
pnputil /add-driver C:\drivers\vendor-nic\*.inf /install
pnputil /enum-drivers

On Server Core, Add-WindowsDriver (DISM) targets offline images; pnputil targets the running OS. Use pnputil for live hosts. For Hyper-V and storage hosts, pin firmware/driver bundles in your image pipeline so a pnputil drift never silently diverges nodes in the same cluster.

7. Remote event log, performance, and storage management

You lost Event Viewer, Performance Monitor, and Disk Management as windows - but every one of them is an object pipeline now, which is strictly more powerful for a fleet.

Event logs - query the modern channels with Get-WinEvent and an XPath/hashtable filter so you pull only what matters across many nodes:

Invoke-Command -ComputerName srv-core-01,srv-core-02 -UseSSL {
  Get-WinEvent -FilterHashtable @{
    LogName   = 'System'
    Level     = 1,2          # Critical, Error
    StartTime = (Get-Date).AddHours(-24)
  } -ErrorAction SilentlyContinue
} | Select-Object PSComputerName, TimeCreated, Id, ProviderName, Message

Performance - pull live counters without opening perfmon. Sample CPU, memory, and disk queue against a node:

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 replaces Disk Management. Initialize a new disk, partition it, and format it end to end:

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
}

WAC surfaces all three of these as dashboards, but the PowerShell path is what you wire into monitoring and runbooks. The GUI is for investigation; the pipeline is for automation.

Enterprise scenario

A platform team running a 200-node Hyper-V and file-server estate standardized on Server Core to cut their patch surface. The build went clean until they fronted it with Windows Admin Center and an operations group 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; 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 reflex fix - unconstrained delegation or CredSSP everywhere - was rejected in review, because unconstrained delegation lets a compromised intermediary impersonate the operator against any service in the forest. The correct, scoped fix is resource-based constrained delegation (RBCD), configured on the resource (the back-end node) to trust only the specific front-end nodes:

# On a DC / management host: let SRV-CORE-01 and -02 delegate to the back-end file server
$frontends = Get-ADComputer -Filter 'Name -like "SRV-CORE-0*"'
Set-ADComputer -Identity "SRV-FILE-BACK01" `
  -PrincipalsAllowedToDelegateToAccount $frontends

RBCD is administered on the resource owner’s side, so the team owning 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. After clearing the 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 finding never had to be opened. The lesson: when a headless, gateway-fronted fleet hits a double-hop wall, reach for RBCD scoped to named principals before you ever consider CredSSP or unconstrained delegation.

Verify

Confirm the listener, the gateway, the JEA constraint, and remote management independently - do not assume a step worked because the previous one did.

# 1. HTTPS listener is up and HTTP is gone
Get-ChildItem WSMan:\localhost\Listener |
  Select-Object @{N='Transport';E={$_.Keys}}, @{N='Port';E={(Get-Item "$($_.PSPath)\Port").Value}}

# 2. End-to-end remoting over TLS succeeds
Test-WSMan -ComputerName srv-core-01.corp.example.com -UseSSL

# 3. JEA endpoint is registered and constrained
Get-PSSessionConfiguration -Name "ServiceOps" |
  Select-Object Name, RunAsVirtualAccount, PSSessionType

# 4. From the operator's account, only whitelisted cmdlets are visible
Invoke-Command -ComputerName srv-core-01.corp.example.com -UseSSL `
  -ConfigurationName "ServiceOps" { Get-Command } |
  Select-Object Name

On the gateway, confirm the node inventory imported and that WAC can reach each one:

Import-Module "$env:ProgramFiles\WindowsAdminCenter\PowerShell\Modules\ConnectionTools"
Get-Connection -GatewayEndpoint "https://wac.corp.example.com" |
  Select-Object Name, Type

If Test-WSMan -UseSSL fails but the HTTP test passes, the certificate is the problem 90% of the time - wrong FQDN in the subject/SAN, expired, or an untrusted issuing CA on the client. Check it before touching anything else:

Get-ChildItem Cert:\LocalMachine\My |
  Select-Object Subject, NotAfter, Thumbprint, EnhancedKeyUsageList

8. Troubleshooting connectivity, certificates, and Kerberos delegation

A short field guide to the failures you will actually hit:

Symptom Likely cause Fix
Test-WSMan -UseSSL fails, HTTP works Cert FQDN/SAN mismatch, expired, or untrusted CA Reissue with correct DNS name; ensure client trusts the CA
Connecting to remote server failed ... Access is denied Kerberos can’t resolve the SPN; using IP not FQDN Connect by FQDN; verify HTTP/<fqdn> SPN with setspn -L
WinRM 0x80090322 on multi-server WAC tools Kerberos double-hop Configure RBCD on the resource (see scenario)
WAC node shows “not accessible” Firewall 5986 closed, or listener bound to wrong cert Open 5986; rebind listener to valid thumbprint
Workgroup/non-domain node unreachable No mutual Kerberos; needs TrustedHosts Add to WSMan:\localhost\Client\TrustedHosts + HTTPS

When Kerberos is suspect, the fastest diagnosis is the ticket cache. A stale or missing service ticket explains most “access is denied” errors after a delegation change:

klist purge          # clear stale tickets on the client/front-end
klist get HTTP/srv-core-01.corp.example.com   # force a fresh ticket for the SPN

For a non-domain node managed through WAC, you must explicitly trust it and use HTTPS, because there is no Kerberos to provide mutual auth:

# On the gateway/admin host - scope TrustedHosts narrowly, never use '*'
Set-Item WSMan:\localhost\Client\TrustedHosts -Value "edge-core-09.dmz.example.com" -Concatenate

TrustedHosts set to * is a finding waiting to happen - it tells WinRM to trust the identity of any host you connect to. Always enumerate specific FQDNs, and prefer bringing the node into a domain or using certificate-based auth over relying on TrustedHosts at all.

Operations checklist

Pitfalls and next steps

The predictable failure modes: choosing Desktop Experience “to be safe” and inheriting a patch surface you did not need; leaving the WinRM HTTP listener enabled so tooling silently falls back off TLS; standing up WAC with a self-signed cert and training operators to click through warnings; and granting local admin for tasks a JEA role should cover. Each is avoidable with the steps above.

For next steps, push JEA further - define per-role capability files for your DNS, file-server, and Hyper-V operators so no human holds standing admin, and feed the transcripts into your SIEM as a privileged-access audit trail. Then evaluate managing the same fleet from Azure: WAC can register with Azure Arc-enabled servers, giving you the browser experience from the Azure portal with Entra ID authentication and conditional access in front of it - the logical endpoint for a headless fleet you want to operate without ever opening an inbound management port to the internet.

windows-serverserver-corewindows-admin-centerpowershellremote-management

Comments

Keep Reading