Austin Downing
Austin Downing

Reputation: 59

Trying to create a powershell user creation script, want to append number to username if it already exists

I am attempting to create a user creation script as a way to teach myself more Powershell. Currently I am only working on creating just the username and want to make sure each username is unique.

After a user inputs the name and number of a user the script should do the following.

Get the the first name Get the middle initial Get the last name Combine the first letter of the first name + middle initial + 6 characters from the last name If users already exists, add number starting at 1 until username is unique. I am currently stuck on step 5. If the username is not unique it appends a number one. I.E. User Brenda T Follower has a username of BTFollow and if that username exists already it becomes BTFollow1.

However if BTFollow and BTFollow1 already exist instead of making BTFollow2 it makes BTFollow12.

Lastly, although not a huge issue I want my parameters to show what comes after Read-Host but that text is not appearing only the variable name.

Here is my code.

Param(
#Gather users first name, required input and must not be empty or null
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[string]
$FirstName = (Read-Host -Prompt 'Please input the users first name.'),

#Gather users middle initial, required input and must not be empty or null and must only be one character
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[ValidateLength(1,1)]
[string]
$MiddleInitial = (Read-Host -Prompt 'Please input the users middle initial.'),

#Gather users last name, required input and must not be empty or null
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[string]
$LastName = (Read-Host -Prompt 'Please input the users last name.'),

#Gathers user phone extension, required input, mustn ot be empty or null, and must only user numbers
[Parameter(Mandatory=$True)]
[ValidateNotNullOrEmpty()]
[ValidatePattern("[0-9][0-9][0-9][0-9]")]
[ValidateLength(4,4)]
[String]
$PhoneExtension = (Read-Host -Prompt 'Please input the users 4 digit exension, numbers only')
)

$i = 0



#Create user name
$Username = $FirstName.Substring(0,1) + $MiddleInitial + $LastName.Substring(0,6)

#Check username does not exist, if it does add numbers

Do {
    Try {
        Get-ADUser $UserName | Out-Null
        $UserName = $Username + ++$i
        Continue
    }
    Catch {
        Break
    }
} While ($True)

Write-Host "Username is $Username"

Upvotes: 1

Views: 1689

Answers (2)

mklement0
mklement0

Reputation: 437813

Because you iteratively modified $UserName in your loop, you ended up appending an additional number in each iteration:

$UserName = $Username + ++$i

With BTFollow as the original value, the value is BTFollow1 after the first iteration, and in the second iteration you then append 2 to that, resulting in BTFollow12, and so on.

Your own answer works (though -like should be replaced with -eq) and, given its appealing concision and the presumed relative rarity of duplicate names, is probably the way to go.

The following solution provides optimizations, but the required complexity may not be worth it; at least I hope it showcases some interesting techniques.


You can avoid a costly loop around Get-AdUser altogether, by pre-filtering potential duplicates with Get-ADUser -Filter and then processing the candidates locally:

$highestIndexSoFar =
  Get-ADUser -Filter "SamAccountName -like '$UserName*'" | 
    ForEach-Object { if ($_.SamAccountName -match '\d*$') { [int] $Matches[0] } } |
      Sort-Object -Descending | Select-Object -First 1

if ($null -eq $highestIndexSoFar) { # no duplicates -> use as-is
    $UniqueUserName = $UserName
} else { # increment the highest index to date to make the username unique
    $UniqueUserName = $UserName + (1 + $highestIndexSoFar)
}

Another advantage of the above solution is that it determines the highest number suffix in the actual names, which reliably uses a suffix that is the highest to date + 1, whereas the call-Get-AdUser-in-a-loop approach uses the first index that isn't taken (though that would only be a concern if users got deleted over time or if out-of-sequence suffixes were manually created).

  • Get-ADUser -Filter "SamAccountName -like '$UserName*'" retrieves potential duplicates; the -Filter syntax isn't sophisticated enough to allow regex-based matching, so this pre-filtering can only find all usernames that share the same prefix, which may include false positives (e.g., jdoet for jdoe, whereas ultimately only a numeric suffix such as jdoe1 should be considered).

  • Regex '\d*$' is then used to weed out such false positives, and the actual, potentially empty numeric suffix (\d*) is reflected in the automatic $Matches variable's [0] entry.

  • The numeric suffix, if present or empty, is then cast to [int] and output.

    • Note that an initially unindexed username such as BTFollow will cause $Matches[0] to be the empty string, which cast [int] converts to 0, so the first index appended will be 1.
  • Sort-Object -Descending sorts the resulting numbers in descending order, and Select-Object -First 1 then extracts the first - i.e. the highest - suffix number found, if any.

  • If no matches are found at all, i.e. if the username isn't taken yet, there is nothing to sort, and Select-Object -First 1 effectively yields $null; otherwise, 1 must be added to highest number to form the new unique username.

Caveat: This approach may still fail if others are creating users simultaneously.

Upvotes: 2

Austin Downing
Austin Downing

Reputation: 59

Figured it out with the help of Reddit. I needed to replace my current code.

Do {
    Try {
        Get-ADUser $UserName | Out-Null
        $UserName = $Username + ++$i
        Continue
    }
    Catch {
        Break
    }
} While ($True)

New code that works.

#Check username does not exist, if it does add numbers
$UniqueUserName = $Username 
while (Get-ADUser -Filter "SamAccountName -like '$UniqueUserName'"){
  $UniqueUserName = $Username + ++$i
}

Upvotes: 2

Related Questions