Created
October 8, 2019 19:21
-
-
Save Bill-Stewart/0ba64bf74206b0d79730f12538167501 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# New-LocalUserAccount.ps1 | |
# Written by Bill Stewart ([email protected]) | |
# | |
# 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