RMM Script: The Bulletproof PowerShell Local Admin Creation Script
This "one-and-done" PowerShell script creates a local user, elevates them to Admin, and sets the password to never expire—perfect for mass deployment via your RMM.
8 min. read
The Ticket: The Locked-Out Emergency
It happened again... a Tier 1 tech accidentally disabled the only known local admin account on an isolated client site, or the domain trust is broken and LAPS is failing to check in. You need a backdoor, immediately. Whether you're deploying a new workstation from scratch or recovering a system with corrupted profile issues, you need a single, reliable payload you can fire blindly from your RMM to create an elevated local admin, set a static password, and bypass expiration policies.
Pre-Flight Check
- Permissions:
NT AUTHORITY\SYSTEM(via RMM execution) or existing Local Administrator. - Tools: PowerShell 5.1+, RMM Scripting Engine (Datto, Ninja, Syncro, Halo).
- Impact: Low - Silent background execution. No reboot required. User sessions remain uninterrupted.
The Solution
PowerShell
# --- 404 & More: Local Admin Creation Script v2.0 ---
$Username = "MSP_Admin"
$Password = "SecurePassword123!"
$Description = "Managed Service Provider Local Admin Account"
# 1. Admin Rights Check: Direct .NET token validation
if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]"Administrator")) {
Write-Host "CRITICAL ERROR: Insufficient privileges. Relaunch as Administrator or SYSTEM." -ForegroundColor Red
exit 1
}
# 2. Convert password to SecureString to prevent plaintext memory lingering
$SecurePassword = ConvertTo-SecureString $Password -AsPlainText -Force
# 3. Create or Update User Logic
if (Get-LocalUser -Name $Username -ErrorAction SilentlyContinue) {
Write-Host "User $Username already exists. Refreshing credentials and flags..."
Set-LocalUser -Name $Username -Password $SecurePassword
Set-LocalUser -Name $Username -PasswordNeverExpires $true
} else {
Write-Host "Creating new user: $Username..."
New-LocalUser -Name $Username -Password $SecurePassword -Description $Description -PasswordNeverExpires $true
}
# 4. Group Elevation
Write-Host "Elevating $Username to Local Administrator..."
Add-LocalGroupMember -Group "Administrators" -Member $Username -ErrorAction SilentlyContinue
Write-Host "Success! Local Admin payload executed." -ForegroundColor Green
The "Why" (Root Cause)
Why are we using PowerShell instead of the old net user MSP_Admin Password! /add batch command? Because legacy batch files are inherently fragile when it comes to password complexity. If your client's compliance policy requires a password with an ampersand (&) or a pipe (|), the standard command prompt interprets those as logical operators, immediately terminating or redirecting the command mid-execution. Your script reports "Success" to the RMM, but the password is truncated or corrupted, permanently locking you out when you actually try to use it.
Furthermore, PowerShell's Microsoft.PowerShell.LocalAccounts module interacts directly with the Security Account Manager (SAM) database using proper API calls rather than parsing sloppy command-line string arguments. By forcing the plaintext password into a SecureString object first, we prevent the unencrypted string from sitting in memory dumps or lingering in transcript logs where a local user running a basic script-kiddie scraper could grab it. We aren't just creating a backdoor; we are doing it cleanly without leaving breadcrumbs.
Under the Hood (Technical Deep Dive)
Let’s break down exactly what the OS is doing when this payload drops.
First, the privileges check. We do not use the basic PowerShell IsElevated wrappers. We invoke the .NET framework directly via [Security.Principal.WindowsIdentity]::GetCurrent(). This queries the access token of the current thread and verifies it holds the well-known SID for BUILTIN\Administrators (S-1-5-32-544). If the RMM misfires and runs this as a standard user context, the script halts immediately with an exit code, preventing useless red-text error spam in your console.
Next, the Get-LocalUser check utilizes -ErrorAction SilentlyContinue. This is entirely intentional. If the user doesn't exist, PowerShell naturally throws a terminating error. We suppress this so the if/else logic can gracefully cascade into the New-LocalUser creation phase. If the user does exist (e.g., a tech previously ran the script but forgot the password), we don't try to recreate it, we simply update the SecureString hash and forcefully refresh the expiration flags.
Why is -PasswordNeverExpires $true so critical? By default, Windows 10/11 respects the local security policy for password maximum age (usually 42 or 90 days). If you deploy a backdoor admin account and don't touch it for six months, the next time you try to log in during a fire, Windows will force a password change at the lock screen. If you don't know the current password (because it was set by a different tech who left your MSP months ago), you are locked out. This flag updates the USER_ACCOUNT_CONTROL attribute directly in the SAM database, flipping the DONT_EXPIRE_PASSWORD bit (0x10000) so your backdoor is always unlocked.
Finally, the elevation command Add-LocalGroupMember ties the new user SID to the Administrators group. Again, we use -ErrorAction SilentlyContinue. If the user is already in the group, it throws a non-terminating error that clutters your RMM logs. We keep the standard output perfectly sanitized.
RMM & Automation Tips
Hardcoding $Password = "SecurePassword123!" in a script deployed to 500 endpoints is a massive, unforgivable security liability. If one machine is compromised, the threat actor now has the local admin password for your entire client base.
- Variable Substitution: Replace the static string with your RMM's environmental variables (e.g.,
$Password = $env:RMM_LOCAL_ADMIN_PASS). Set this variable dynamically per client or per site inside your dashboard. This way, the script execution pulls the variable dynamically, and the password is never stored in the raw.ps1file. - The "Hidden" Admin: If you want to keep the login screen clean for the client and prevent them from asking "Who is MSP_Admin?", hide the account from the Windows Welcome screen. Add this one-liner to the bottom of your script to inject a registry key:
New-Item -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" -Force | Out-Null; New-ItemProperty -Path "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\SpecialAccounts\UserList" -Name $Username -PropertyType DWord -Value 0 -ForceThe account still works flawlessly via UAC prompts and RDP, but it's invisible to the naked eye at the physical terminal.
Troubleshooting & Edge Cases
- Edge Case 1: LAPS (Local Administrator Password Solution) Conflicts: If Microsoft LAPS is actively managing the exact account name you specify in
$Username, LAPS will overwrite your password on its next GPUpdate cycle. Always use a dedicated MSP account name that falls outside the LAPS target scope. - Edge Case 2: Restricted Groups GPO Overrides: If the client has a strict domain GPO enforcing "Restricted Groups" for local Administrators, your newly created user will be forcefully stripped from the local admin group within 90 minutes. You must add your MSP admin account to the domain-level GPO to make the elevation stick permanently.