A fresh Windows Server VPS is configured for a lab, not for the internet. Remote Desktop is wide open, SMBv1 may be enabled for legacy compatibility, local administrator is still called Administrator, and no audit policy is collecting evidence. This is a hardening baseline: a concrete, ordered checklist you can run in one sitting against a new Windows Server 2019/2022/2025 instance, with the commands and policy paths that make each item verifiable rather than aspirational.
If you are still provisioning, the baseline is far easier to apply before applications are installed. For background on the platform itself, see our Windows VPS guide; for the access layer specifically, securing RDP on a Windows VPS covers NLA and certificate binding in more depth.
Phase 1: Establish access you cannot lose
Every hardening change carries a lockout risk. Before touching the firewall, confirm you have out-of-band console access through the provider panel, and create a second local administrator account as a break-glass path.
- ☐ Confirm provider console/KVM access works, not just RDP.
- ☐ Create a named break-glass admin account with a unique long password, stored in a password manager.
- ☐ Do not rename or disable the built-in Administrator until the new account is proven to log on.
- ☐ Document the exact firewall rules you are about to change so you can revert them.
$pwd = Read-Host -AsSecureString "Break-glass password"
New-LocalUser -Name "bkadmin" -Password $pwd -PasswordNeverExpires:$false -AccountNeverExpires
Add-LocalGroupMember -Group "Administrators" -Member "bkadmin"
Add-LocalGroupMember -Group "Remote Desktop Users" -Member "bkadmin"
Phase 2: Windows Firewall — enable all three profiles
Domain, Private and Public profiles should all be enabled with inbound-block by default. On a VPS the network location may be classified as Public, so leaving that profile off is the most common mistake.
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled True -DefaultInboundAction Block -DefaultOutboundAction Allow
Get-NetFirewallProfile | Select-Object Name, Enabled, DefaultInboundAction
# Explicit allow for administrative access, restricted to known source IPs
New-NetFirewallRule -DisplayName "RDP - Admin IPs only" -Direction Inbound -Protocol TCP -LocalPort 3389 `
-RemoteAddress 198.51.100.10,198.51.100.11 -Action Allow -Profile Any
# Remove the default allow-any RDP rule so it cannot override the scoped one
Get-NetFirewallRule -DisplayGroup "Remote Desktop" | Disable-NetFirewallRule
- ☐ All three profiles Enabled = True.
- ☐ Inbound default action = Block.
- ☐ RDP restricted to named source addresses, with a separate rule per admin location.
- ☐ Unused roles’ inbound rules disabled (WinRM 5985/5986, SMB 445, RPC dynamic ports).
- ☐ Logging enabled:
Set-NetFirewallProfile -Profile Any -LogBlocked True -LogFileName %systemroot%\system32\LogFiles\Firewall\pfirewall.log -LogMaxSizeKilobytes 16384.
Phase 3: RDP hardening
RDP is the most attacked service on any Windows VPS. Three settings carry most of the benefit: Network Level Authentication, a TLS security layer with high encryption, and restricted logon rights.
$rdp = "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp"
Set-ItemProperty $rdp -Name UserAuthentication -Value 1 # Require NLA
Set-ItemProperty $rdp -Name SecurityLayer -Value 2 # TLS 1.0+
Set-ItemProperty $rdp -Name MinEncryptionLevel -Value 3 # High
Set-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server" -Name fDenyTSConnections -Value 0
# Audit RDP logons
auditpol /set /subcategory:"Logon" /success:enable /failure:enable
auditpol /set /subcategory:"Special Logon" /success:enable /failure:enable
# Lockout policy to blunt password spraying
net accounts /lockoutthreshold:5 /lockoutduration:15 /lockoutwindow:15
If your provider offers a VPN or a jump host, prefer it: expose RDP only to the VPN subnet and close 3389 to the public internet entirely. This single change eliminates the bulk of internet-facing RDP attack traffic.
- ☐ NLA required (
UserAuthentication = 1). - ☐ Security layer = TLS, encryption level = High.
- ☐ Account lockout threshold ≤ 5 attempts.
- ☐ Remote Desktop Users group contains only accounts that need interactive sessions.
- ☐ Consider a port change only as obfuscation, never as a substitute for NLA.
Phase 4: Microsoft Defender and attack surface reduction
# Verify protection is on (it is enabled by default on Server 2016+)
Get-MpComputerStatus | Select-Object RealTimeProtectionEnabled, AntivirusEnabled, AMServiceEnabled
# Enable cloud protection and sample submission
Set-MpPreference -MAPSReporting Advanced -SubmitSamplesConsent SendSafeSamples
# Disable Office child-process and script-from-email vectors
Add-MpPreference -AttackSurfaceReductionRules_Ids BE9BA2D9-53EA-4CDC-84E5-9B1EEEE46550 -AttackSurfaceReductionRules_Actions Enabled
Add-MpPreference -AttackSurfaceReductionRules_Ids D3E037E1-3EB8-44C8-A917-57927947596D -AttackSurfaceReductionRules_Actions Enabled
Add-MpPreference -AttackSurfaceReductionRules_Ids 56A863A9-875E-4185-98A7-B882C64B5CE5 -AttackSurfaceReductionRules_Actions Enabled
# Run a full scan and check exclusions are not over-broad
Start-MpScan -ScanType FullScan
- ☐ Real-time protection enabled.
- ☐ Cloud-delivered protection set to Advanced.
- ☐ ASR rules for Office child processes, process creation from PSExec/WMI, and untrusted executable execution.
- ☐ Exclusions reviewed — never exclude entire drives or
C:\. - ☐ Scheduled quick scan at least daily.
Phase 5: Protocol and legacy service removal
# Disable SMBv1 (client and server)
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
Disable-WindowsOptionalFeature -Online -FeatureName SMB1Protocol -NoRestart
# Disable LLMNR and NetBIOS over TCP/IP poisoning vectors
New-Item "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" -Force | Out-Null
Set-ItemProperty "HKLM:\SOFTWARE\Policies\Microsoft\Windows NT\DNSClient" -Name EnableMulticast -Value 0
# Disable SMB signing bypass and enable signing
Set-SmbServerConfiguration -RequireSecuritySignature $true -EnableSecuritySignature $true -Force
# Stop and disable unneeded services
'RemoteRegistry','Spooler','WinRM' | ForEach-Object {
Stop-Service $_ -Force -ErrorAction SilentlyContinue
Set-Service $_ -StartupType Disabled -ErrorAction SilentlyContinue
}
- ☐ SMBv1 disabled (verify with
Get-WindowsOptionalFeature -Online -FeatureName SMB1Protocol). - ☐ LLMNR and NetBIOS disabled where not required.
- ☐ SMB signing required.
- ☐ Print Spooler disabled unless the server actually prints (PrintNightmare mitigation).
- ☐ Remote Registry and unused remote-management endpoints disabled.
Phase 6: Update policy and patch hygiene
An unpatched server is the fastest route to compromise, and a rebooted server in the middle of the business day is the fastest route to an unhappy stakeholder. Configure a maintenance window explicitly.
# Check current patch state
Get-WindowsUpdateLog -LogPath C:\temp\wu.log # or, with the PSWindowsUpdate module:
Get-WUList -MicrosoftUpdate
# Set an active hours window via policy so patches install and reboot out of hours
$au = "HKLM:\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU"
New-Item $au -Force | Out-Null
Set-ItemProperty $au -Name NoAutoRebootWithLoggedOnUsers -Value 1
Set-ItemProperty $au -Name AUOptions -Value 4 # auto download and schedule install
- ☐ Monthly cumulative updates installed within your patch SLA.
- ☐ Automatic reboot scheduled outside business hours.
- ☐ Pre-patch snapshot or backup taken for role servers.
- ☐ Server rebooted and verified after each patch cycle — not just “installed”.
Phase 7: Accounts, auditing and monitoring
- ☐ Built-in Administrator renamed; no account named “Administrator” is reachable by RDP.
- ☐ No shared credentials; each admin has a named account.
- ☐ LAPS or equivalent in use if the server is domain-joined.
- ☐ Advanced Audit Policy enabled for logon, account management and object access.
- ☐ Event log sizes increased (Security to 512 MB+) so evidence survives past a few days.
- ☐ Failed RDP attempts reviewed weekly — a spike in 4625 means you are being sprayed.
# Grow log sizes so you have retention
wevtutil sl Security /ms:536870912
wevtutil sl System /ms:134217728
wevtutil sl Application /ms:134217728
# Weekly quick review of RDP brute-force attempts
Get-WinEvent -FilterHashtable @{LogName='Security'; Id=4625; StartTime=(Get-Date).AddDays(-7)} |
Group-Object { $_.Properties[5].Value } | Sort-Object Count -Descending | Select-Object -First 10 Count, Name
The order that matters
If you only have an hour, do these in order: confirm console access, enable all firewall profiles and scope RDP to known IPs, require NLA, disable SMBv1, turn on Defender cloud protection, then set an out-of-hours patch window. That sequence closes the highest-risk exposures first without ever risking a permanent lockout.
Then document the baseline and re-check it quarterly, because hardening drifts: an application installer re-enables SMBv1, a support vendor asks for RDP from an any-IP rule, and a role server accumulates exceptions. If you would rather start from a platform where the baseline is already sane, compare Windows VPS options that ship with hardened images and out-of-band console access, and pair this checklist with our guide to Windows Firewall rules and hardening for the network layer.



