3

I am scripting some code that will help my team automate some AD User mod tasks and ensure that we are doing things consistently. I am prompting for a user's last name (or partial last name) and returning a list of matching users (Index, Name, userID) to ensure we mod the correct account.

For example if I search for Smith it would return:

[0] Smith, John   jsmith
[1] Smith, Jane   jsmith1
[2] Smithfield, Robert  rsmithfield

The tech would then need to enter the corresponding number of the account they want to modify. I put in an input validation check to ensure that a valid index number is chosen (in this case it will not accept a negative int or an int greater than 2).

Unfortunately I do not know how to also ensure that a letter or symbol is not entered. In this case if users enter the number 3 it will return a message of "Invalid Selection" but if they enter the letter d, it accepts it and then errors out.

How do I make it so it will only accept an int?

FYI - after I get this working I will be creating functions and function calls so it would be more modular, this is just initial testing of the concept.

#prompt for last name (or at least portion of last name, then conver to a string with wild card for use in filter
$lname = Read-Host "`nEnter at least the first three chars of users last name (you may enter full last name)"
$search = $lname + "*"

#Empty arrays
$ResultsArray=@("")
$ADUsersList=@("")

#add aduser properties to array, return error and exit if no user found
$ADUsersList += Get-ADUser -filter 'surname -like $search' -Properties * | select name,samaccountname,DistinguishedName
if ($ADUsersList.count -le 1){
    Write-host "USER NOT FOUND"
    return
}

#populate a 2D array with NAM and with UserID
$ResultsArray = @(($ADUsersList.name),($ADUsersList.samaccountname))

#return list of found users and user ids. Preface with index number to use for selection menu
for($i=0;$i-le $ResultsArray[0].length-1;$i++){
    “[{0}] = {1} `t {2}” -f $i,$ResultsArray[0][$i],$ResultsArray[1][$i]
}

#Prompt 
[int]$selection = Read-Host "`nEnter number for user you want to modify"

#Input validation - Only allow a valid index #.  If valid, write user selected to screen before proceeding 
if ($selection -gt $ResultsArray[0].length -or $selection -lt 0){
    Write-host "INVALID SELECTION!!!" -ForegroundColor Red
} 
else {
    Write-Host "`nYou selected user " -nonewline 
    Write-Host $ResultsArray[0][$selection] -nonewline -ForegroundColor Green
    Write-Host " with user ID a of " -nonewline 
    Write-Host $ResultsArray[1][$selection] -nonewline -ForegroundColor Green
}

2
  • Try using a more formal PowerShell prompt, as shown in this example: PowerShell get the same Read-Host if wrong input Commented Nov 7, 2023 at 21:42
  • A couple of inconsequential asides: Technically, $ResultsArray is a jagged array, not a 2D array. Also, you don't need to initialize your arrays separately; e.g., $ADUsersList = Get-ADUser ... will do (or, if you want to ensure that the result is an array even in the event that only one user is returned: $ADUsersList = @(Get-ADUser ...)) Commented Nov 8, 2023 at 18:42

2 Answers 2

2

This might be an easier way to do it, you can use the -as operator for safe conversion and a regex pattern to ensure that the input was a numeric digit:

$testCollection = 0..(Get-Random -Maximum 10)
for ($i = 0; $i -le $testCollection.Count - 1; $i++) {
    '[{0}] = {1}' -f $i, $testCollection[$i]
}
$selection = (Read-Host "`nEnter number for user you want to modify") -as [int]

if ($selection -notmatch '^[0-9]+$' -or $selection -ge $testCollection.Count) {
    return Write-Warning 'Invalid stuff..'
}
$testCollection[$selection]

But even simpler and more robust would be to use Out-GridView -PassThru:

$selection = Get-ADUser -Filter "surname -like '$search'" |
    Select-Object name, samaccountname, DistinguishedName |
    Out-GridView -PassThru -Title 'Choose a user'

if (-not $selection) {
    return 'Nothing was selected...'
}

Write-Host "`nYou selected user " -NoNewline 
Write-Host $selection.Name -NoNewline -ForegroundColor Green
Write-Host ' with user ID a of ' -NoNewline 
Write-Host $selection.samAccountName -NoNewline -ForegroundColor Green
Sign up to request clarification or add additional context in comments.

4 Comments

@n0rd int32 has negative numbers too. it wouldnt make sense if I was using uint32 but im not, I decided to remove the cast to uint32 to not confuse OP.
Are you seriously checking for negative numbers with regex?.. Also -as will not return $null for failed conversions, it will return 0, which might lead to unexpected results.
'a string' -as [int] is $null not 0 isn't it? @n0rd the current code as is will not produce unexpected results, the regex is ensuring that, maybe you should try it yourself.
Indeed re $null; I missed that due to use of [int] with -as (rather than [Uint32]) , $selection does receive negative numbers as such, so the -notmatch operation is successful in weeding out negative numbers. An alternative is to stay in the realm of numbers (relying on $null -lt 1 being $true): $selection -lt 1 -or $selection -ge $testCollection.Count. /cc @n0rd
0

OutGridView -PassThru, as suggested in Santiago's helpful answer, is an elegant solution that bypasses your validation problem.

However, it involves pop-up of a GUI, which may not be desired.

An alternative way to handle this in the console (terminal) is to test the user input for being contained in the array of valid indices, using the -in operator.

A simplified example:

if (($num = Read-Host 'Enter a number between 1 and 3') -in (1..3)) {
  # Now it's safe to convert the string input to [int]
  $num = [int] $num 
  "You chose index $num."
} else {
  Write-Warning "'$num' is not a number between 1 and 3"
}

Spelled out in the context of (a simplified version of) your code:

# Sample jagged results array.
$ResultsArray = ('foo', 'bar', 'baz'), ('...')

# Get the list of valid (1-based) indices.
$validIndices = 1..$ResultsArray[0].Count

# ... print the index-prefixed list of results.

# Keep prompting until the user's input is valid index.
while ($true) {
  $userInput = Read-Host "`nEnter number for user you want to modify"
  if ($userInput -in $validIndices) {
     $selectedIndex = [int] $userInput
     break
  } else {
     Write-Warning "'$userInput' is not a number between 1 and $($validIndices.Count). Please try again."
  }
}

# Getting here means that a valid choice was made.
Write-Host "Selected index: $selectedIndex"
# ...

Note:

  • Even though Read-Host's output is invariably a string, whereas the elements of the array stored in $validIndices are of type [int], use of the -in operator still works as expected (the same applies analogously to the operands-reversed -contains operator):

    • PowerShell tries to convert the LHS operand to each RHS element's data type, if necessary:

      • If the conversion fails, the comparison is (quietly) considered to be $false

      • If the conversion succeeds, the comparison is performed based on the conversion result.

      • (Either way, an element-individual comparison yielding $true short-circuits the operation and returns $true; otherwise, element-by-element comparison continues.)

    • Note that the above implies that string input such as '0x2' and '+2' would match an [int] array element of value 2 as well.
      Surrounding whitespace is always ignored (e.g, ' 2 ' matches a 2 element too).

Comments

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.