<# .SYNOPSIS Creates a local administrator account on this computer, with the password typed in securely and an optional expiry date. .DESCRIPTION For the one-off cases: a lab box, a standalone server, a temporary account for a vendor. Creates the account, adds it to the local Administrators group by SID (so it works on non-English Windows), and returns the new account. If you don't pass -Password, it prompts twice and makes sure both match. The password is never written to disk or shown. For fleet-wide admin accounts, use Windows LAPS. .PARAMETER Name The new account name. 20 characters max. .PARAMETER Password The password as a SecureString. Leave it off to be prompted. .PARAMETER FullName Display name for the account. .PARAMETER Description Description shown on the account. .PARAMETER AccountExpires Date the account stops working. Leave it off for an account that never expires. .EXAMPLE .\New-LocalAdminAccount.ps1 -Name labadmin .EXAMPLE .\New-LocalAdminAccount.ps1 -Name vendor-tmp -FullName 'Vendor support' -AccountExpires (Get-Date).AddDays(3) #> [CmdletBinding(SupportsShouldProcess)] param( [Parameter(Mandatory)] [ValidateLength(1, 20)] [ValidatePattern('^[^\\/\[\]:;|=,+*?<>@"]+$')] [string]$Name, [securestring]$Password, [string]$FullName = '', [ValidateLength(0, 48)][string]$Description = 'Local administrator', [datetime]$AccountExpires ) function Test-IsElevated { $principal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator) } function Compare-SecureString { param([securestring]$First, [securestring]$Second) $a = [System.Net.NetworkCredential]::new('', $First).Password $b = [System.Net.NetworkCredential]::new('', $Second).Password try { $a -ceq $b } finally { $a = $null; $b = $null } } if (-not (Test-IsElevated)) { throw 'Run this from an elevated PowerShell session.' } if (Get-LocalUser -Name $Name -ErrorAction SilentlyContinue) { throw "A local account named '$Name' already exists." } if ($PSBoundParameters.ContainsKey('AccountExpires') -and $AccountExpires -le (Get-Date)) { throw 'AccountExpires is in the past.' } if (-not $Password) { $Password = Read-Host -AsSecureString -Prompt "Password for $Name" $confirm = Read-Host -AsSecureString -Prompt 'Type it again' if (-not (Compare-SecureString -First $Password -Second $confirm)) { throw "The passwords didn't match. Nothing was created." } } if ($Password.Length -lt 14) { Write-Warning 'That password is under 14 characters. Fine for a lab, not for anything that matters.' } $userArgs = @{ Name = $Name Password = $Password Description = $Description ErrorAction = 'Stop' } if ($FullName) { $userArgs.FullName = $FullName } if ($PSBoundParameters.ContainsKey('AccountExpires')) { $userArgs.AccountExpires = $AccountExpires } else { $userArgs.AccountNeverExpires = $true } if (-not $PSCmdlet.ShouldProcess($Name, 'Create local account and add it to Administrators')) { return } $user = New-LocalUser @userArgs Write-Verbose "Created $Name ($($user.SID))" try { Add-LocalGroupMember -SID 'S-1-5-32-544' -Member $user -ErrorAction Stop } catch { Write-Warning "Created $Name but couldn't add it to Administrators: $($_.Exception.Message)" } Get-LocalUser -Name $Name | Select-Object Name, FullName, Enabled, AccountExpires, PasswordLastSet, SID