Skip to content

Instantly share code, notes, and snippets.

@Bill-Stewart
Created October 8, 2019 19:21
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save Bill-Stewart/0ba64bf74206b0d79730f12538167501 to your computer and use it in GitHub Desktop.
Save Bill-Stewart/0ba64bf74206b0d79730f12538167501 to your computer and use it in GitHub Desktop.
# New-LocalUserAccount.ps1
# Written by Bill Stewart (bstewart@iname.com)
#
# The impetus for writing this script is the deprecation of the 'Local Users
# and Groups' Group Policy Preference. You can use this script as a computer
# startup script to safely create a local account on computers.
#
# Version history:
#
# 1.0 (2018-08-20)
# * Initial version.
#
# 1.1 (2018-08-24)
# * Only write a warning rather than throwing a terminating error if user
# already exists. (Throwing a terminating error generates an error event in
# the event log if the script is run as a logon script and could cause
# unnecessary confusion.)
#
# 1.2 (2019-10-04)
# * Add ValidateNotNullOrEmpty parameter validation attribute to -UserName.
# * Standardize code formatting.
#requires -version 2
<#
.SYNOPSIS
Creates a local user account with a random password.
.DESCRIPTION
Creates a local user account with a random password. The password is not retained, so the account's password will have to be reset before the account can be used.
.PARAMETER UserName
Specifies the user name to use for the local account.
.PARAMETER FullName
Specifies the full name of the local user account.
.PARAMETER Description
Specifies the Description property of the local user account.
.PARAMETER ChangePasswordAtLogon
Specifies that the user must change the password at next logon.
.PARAMETER Disabled
Specifies that the account is disabled.
.PARAMETER CannotChangePassword
Specifies that the user cannot change the password for the account.
.PARAMETER PasswordNeverExpires
Specifies that the user account's password never expires.
.INPUTS
None
.OUTPUTS
None
#>
[CmdletBinding(SupportsShouldProcess = $true)]
param(
[Parameter(Mandatory = $true)]
[ValidateNotNullOrEmpty()]
[String] $UserName,
[String] $FullName,
[String] $Description,
[Switch] $ChangePasswordAtLogon,
[Switch] $Disabled,
[Switch] $CannotChangePassword,
[Switch] $PasswordNeverExpires
)
# See https://docs.microsoft.com/en-us/windows/desktop/api/iads/ne-iads-ads_user_flag
$ADS_UF_ACCOUNTDISABLE = 0x00002
$ADS_UF_PASSWD_CANT_CHANGE = 0x00040
$ADS_UF_DONT_EXPIRE_PASSWD = 0x10000
# Returns $true if the local user account exists, or $false otherwise.
function Test-LocalUserAccount {
param(
[String] $userName
)
try {
[ADSI]::Exists("WinNT://./$userName,User")
}
catch [Management.Automation.MethodInvocationException] {
return $false
}
}
# Returns a random string of the specified length.
function Get-RandomString {
param(
[UInt32] $length
)
$byteCount = [Math]::Ceiling((($length * 6) + 7) / 8)
$bytes = New-Object Byte[] $byteCount
$pRNG = New-Object Security.Cryptography.RNGCryptoServiceProvider
$pRNG.GetBytes($bytes)
[Convert]::ToBase64String($bytes).Substring(0,$length)
}
if ( Test-LocalUserAccount $UserName ) {
Write-Warning "Local user account '$UserName' already exists."
return
}
if ( -not $PSCmdlet.ShouldProcess($UserName,"Create local user account") ) {
return
}
$computer = [ADSI] "WinNT://.,Computer"
# User account isn't created until SetInfo method runs.
$localUserAccount = $computer.Create("User",$UserName)
$localUserAccount.SetPassword((Get-RandomString 127))
if ( $FullName ) {
$localUserAccount.Properties["FullName"].Value = $FullName
}
else {
# FullName property is set to the account name by default, so if not
# specified, we'll use an empty string.
$localUserAccount.Properties["FullName"].Value = ""
}
if ( $Description ) {
$localUserAccount.Properties["Description"].Value = $Description
}
if ( $ChangePasswordAtLogon ) {
$localUserAccount.Properties["PasswordExpired"].Value = 1
}
# Throw an error if we can't create the user account.
try {
$localUserAccount.SetInfo()
}
catch [Management.Automation.MethodInvocationException] {
throw $_.Exception.InnerException
}
# Account must exist before we can modify the UserFlags attribute.
$flags = $localUserAccount.Properties["UserFlags"].Value
$newFlags = $flags
if ( $Disabled ) {
$newFlags = $newFlags -bor $ADS_UF_ACCOUNTDISABLE
}
if ( $CannotChangePassword ) {
$newFlags = $newFlags -bor $ADS_UF_PASSWD_CANT_CHANGE
}
if ( $PasswordNeverExpires ) {
$newFlags = $newFlags -bor $ADS_UF_DONT_EXPIRE_PASSWD
}
if ( $newFlags -ne $flags ) {
$localUserAccount.Properties["UserFlags"].Value = $newFlags
# Throw an error if updating the user account fails.
try {
$localUserAccount.SetInfo()
}
catch [Managment.Automation.MethodInvocationException] {
throw $_.Exception.InnerException
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment