1

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"
4
  • take a look at the 1st answer here ... active directory - Powershell AD Script - Add a number onto the end of account Name - Stack Overflow — stackoverflow.com/questions/50694247/… Commented Nov 13, 2018 at 19:18
  • That did not work at all. Commented Nov 13, 2018 at 19:43
  • As for "I want my parameters to show what comes after Read-Host but that text is not appearing only the variable name": It's an interesting problem, but please ask in it in a new, separate question. Commented Nov 13, 2018 at 20:17
  • @AustinDowning - this line $SamAccountName = $defaultname + [string]$i sure seems to work. i guess we must be thinking of different things. however, your answer below sure looks similar ... [grin] Commented Nov 13, 2018 at 22:53

2 Answers 2

2

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
}
Sign up to request clarification or add additional context in comments.

Comments

2

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.

2 Comments

Your filtered solution works great. One question I can't figure out. Assuming you are using a new unique name (no existing match in AD), when you assign the top of the array to $highestIndexSoFar you get an error about indexing a null array. Which is what you would expect and the results are correct. But I HATE error messages. I was trying to use -errorAction SilentlyContinue to get around it, but that doesn't work in that case. Any ideas?
Good point about indexing into the null array, @Dave. Please see my update, which uses Select-Object -First 1 to avoid the problem, and also simplifies the regex.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.