How to protect your administrator account from brute-force lockout
By default, Windows exposes its RDP port to the internet, making it an attractive target for brute-force attacks. When an attack occurs, the administrator account may be blocked after numerous failed login attempts. If this happens, you could lose access to the server until the issue is resolved.
To prevent this, use the PowerShell script below. It changes default RDP settings and makes it difficult for attackers to target you with malicious activity.
How the script works
The script has two sections:
- Configuration section — lets you choose which protections to enable by uncommenting the relevant lines (remove the
#at the start of the line). - Code section — performs the actual work. Do not modify this part.
Insert the script into the user data field when ordering a server or reinstalling an operating system.
The script is designed for Windows Server 2016, 2019, 2022, and 2025.
Available options
| Option | Description |
|---|---|
$AdministratorAccountLoginName | Renames the built-in Administrator account to a custom name. |
$RDPPort | Changes the RDP port from the default 3389 to a custom port. Triggers an additional reboot during installation. |
$TrustedClients | Restricts RDP access to specific trusted IP addresses or ranges. Recommended when connecting from static IPs. |
$EnableAdministratorAccountLockout | Enables automatic lockout of the Administrator account after repeated failed logins (KB5020282). Enable only after creating a separate admin account for daily use. |
$DisableRemoteDesktop | Disables RDP entirely. Recommended if you access the server only via WinRM, SSH, or another protocol. Takes precedence over all other RDP settings. |
User data script
#ps1
# BEGINNING OF THE CONFIGURATION SECTION
# When an RDP port is exposed to the Internet, it may be a target of brute-force attacks.
# This may lead to your server being compromised, administrative account lockouts etc. This script helps you to reduce those risks.
# You may amend the script in accordance with your requirements.
# To enable settings you need, remove the octothorpe (#) symbol from the beginning of that line.
# Renames the built-in Administrator account.
# Make sure that your software / scripts support the built-in Administrator account having a non-default name.
# $AdministratorAccountLoginName = 'ExampleNewLoginName'
# A new RDP port number instead of the default 3389.
# Enabling this parameter triggers an additional reboot during server installation.
# $RDPPort = 3403
# Restricts RDP access only to trusted IP addresses.
# This parameter is recommended when you connect to the server from static IP addresses.
# Do not use if your RDP clients use dynamic IP addresses.
# You can specify here a range of IP addresses that your ISP allocated to you.
# $TrustedClients = @('10.0.0.0/8', '192.0.2.1')
# Enables lockout for the built-in Administrator account.
# In KB5020282 Microsoft introduced a new feature to automatically lockout the Administrator account when a malicious actor tries to brute-force its password.
# While enabling this setting increases security of your system, if your server is exposed to the Internet, the Administrator account will be locked-out almost immediately.
# Therefore, prior to enabling this setting, we recommend you to create a separate account with administrative privileges and use it for daily administrative tasks.
# $EnableAdministratorAccountLockout = $true
# Disables Remote Desktop server.
# We recommend you to enable this setting if you access your server only via WinRM, SSH, or some other protocol that is not RDP.
# This setting takes precedence over the RDP port and trusted clients settings.
# $DisableRemoteDesktop = $true
# END OF THE CONFIGURATION SECTION
#Requires -Version 5
if ($AdministratorAccountLoginName) {
$BuiltInAdministrator = Get-LocalUser | Where-Object -FilterScript { $_.SID -like 'S-1-5-21-*-500' }
Rename-LocalUser -InputObject $BuiltInAdministrator -NewName $AdministratorAccountLoginName
}
if ($EnableAdministratorAccountLockout) {
$SecurityPolicyTempFileName = Join-Path -Path $env:TEMP -ChildPath (New-Guid).Guid
$SecurityDBTempFileName = Join-Path -Path $env:TEMP -ChildPath (New-Guid).Guid
secedit /export /cfg $SecurityPolicyTempFileName /quiet
(Get-Content -Path $SecurityPolicyTempFileName).Replace('AllowAdministratorLockout = 0', 'AllowAdministratorLockout = 1') | Set-Content -Path $SecurityPolicyTempFileName
secedit /configure /db $SecurityDBTempFileName /cfg $SecurityPolicyTempFileName /areas SECURITYPOLICY /quiet
Remove-Item -Path $SecurityPolicyTempFileName -Force
Remove-Item -Path $SecurityDBTempFileName -Force
}
if ($DisableRemoteDesktop) {
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server' -Name 'fDenyTSConnections' -Value 1 -Force
Set-NetFirewallRule -Name 'RemoteDesktop-UserMode-In-TCP' -Enabled False
Set-NetFirewallRule -Name 'RemoteDesktop-UserMode-In-UDP' -Enabled False
}
else {
if ($TrustedClients -or $RDPPort) {
$SetNetFirewallRuleParameters = @{}
if ($TrustedClients) {
$SetNetFirewallRuleParameters.Add('RemoteAddress', $TrustedClients)
}
if ($RDPPort) {
$SetNetFirewallRuleParameters.Add('LocalPort', $RDPPort)
}
Set-NetFirewallRule -Name 'RemoteDesktop-UserMode-In-TCP' -Enabled True @SetNetFirewallRuleParameters
Set-NetFirewallRule -Name 'RemoteDesktop-UserMode-In-UDP' -Enabled True @SetNetFirewallRuleParameters
if ($RDPPort) {
Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp' -Name 'PortNumber' -Value $RDPPort -Force
# Exit code 1001 triggers cloudbase-init to reboot the host after finishing userscripts plugin and not run it again after the reboot.
exit 1001
}
}
}