3

Im a Linux Sys Admin by profession, but am having to do some powershelling. My problem is this:

Im getting a list of active users from the AD with this command:

$allusers= get-aduser -Filter {Enabled -eq $true} | FT samAccountName 

My intent being that $allusers becomes an array filled with the lines of output from the get-aduser command. popping this block just after the array is populated seems to confirm that is indeed the case.

for ($i; $i -le $allusers.length; $i++) {
    $allusers[$i]
}

Great! so now I want to check if a given username exists within that array. I see that arrays include a very handy "Contains()" function, which looks like it should serve my purpose.

echo $allusers.Contains("joeq")

But alas! I get this error:

Method invocation failed because [System.Object[]] doesn't contain a method named 'contains'.

At C:\Users\dylanh\jirauserpurge.ps1:33 char:24 + echo $allusers.contains <<<< ("joeq") + CategoryInfo : InvalidOperation: (contains:String) [], RuntimeException + FullyQualifiedErrorId : MethodNotFound

Hmmm... OK, lets go "old skool"

for ($i; $i -le $allusers.length; $i++) {
    if ($allusers[$i] -eq "joeq") {
        echo "joeq present"
    }
}

The condition is never matched! My brain hurts! OK, Its possible there's some whitespace characters causing mischief here, lets trim:

for ($i; $i -le $allusers.length; $i++) {
    if ($allusers[$i].Trim() -eq "joeq") {
        echo "joeq present"
    }
}

But no, that results in this:

Method invocation failed because [Microsoft.PowerShell.Commands.Internal.Format.FormatEntryData] doesn't contain a method named 'Trim'.
At C:\Users\dylanh\jirauserpurge.ps1:39 char:24
+     if ($allusers[$i].Trim <<<< () -eq "nickf") {
+ CategoryInfo          : InvalidOperation: (Trim:String) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound

Clearly its some kind of type casting problem that I'm missing. What do I need to do to allow searching the "array" for a specific value?

UPDATE: With the help of fdibot's answer and HopelessN00b's comment, I have a working solution:

$allusers= get-aduser -Filter {Enabled -eq $true}
$usernames = @()
for ($i=0; $i -le $allusers.length; $i++) {
    $usernames += $allusers[$i].SamAccountName
}
$usernames -contains "joeq"

The additional for loop seems a bit redundant TBH (or at least it would be in bash!) Ah well! thanks for your input everyone.

5
  • Not to sure because i don't use powershell to often, but shouldn't your array be $allUsers = @(command to populate) to declare an array?
    – Fegnoid
    Oct 23, 2014 at 8:28
  • @Fegnoid. Thanks for the suggestion, but that hasn't made any discernable difference
    – GeoSword
    Oct 23, 2014 at 8:51
  • 2
    As implied, but not explicitly stated in the answer by @fdibot , the issue is that you're actually storing an array of user objects, even if the FT only displays the property you specify. So it's not an array of SAMaccountNames, it's an array of users (which all have a SAMaccountName attribute), and you have to access the properties of those objects accordingly. Oct 23, 2014 at 9:32
  • Slightly unrelated, but suggestions to help you debug when you were initially having problems - using $allusers[$i].GetType() inside your loop might have helped you see the contents of the list weren't string items. And when you tried Contains() and found it wasn't there, $allusers | Get-Member -MemberType Method would have shown all the methods you could use, and helped you see what you were working with. Oct 23, 2014 at 18:22
  • Regarding the last update, you probably want to use Select -Expand SamAccountName and get rid of the loop. This would be the preferred approach using powershell. $allusers= get-aduser -Filter {Enabled -eq $true} | Select -Expand SamAccountName
    – Jacob
    Oct 26, 2014 at 0:34

4 Answers 4

8

The problem comes with your Format-Table :)

$allusers= get-aduser -Filter {Enabled -eq $true} | FT samAccountName
$allusers |  gm

TypeName : Microsoft.PowerShell.Commands.Internal.Format.FormatStartData

Name MemberType Definition ---- ---------- ---------- Equals Method bool Equals(System.Object obj) GetHashCode
Method int GetHashCode() GetType
Method type GetType() ToString
Method string ToString() autosizeInfo
Property Microsoft.PowerShell.Commands.Internal.Format.AutosizeInfo, Syste... ClassId2e4f51ef21dd47e99d3c952918aff9cd Property string ClassId2e4f51ef21dd47e99d3c952918aff9cd {get;} groupingEntry
Property
Microsoft.PowerShell.Commands.Internal.Format.GroupingEntry, Syst... pageFooterEntry Property
Microsoft.PowerShell.Commands.Internal.Format.PageFooterEntry, Sy... pageHeaderEntry Property
Microsoft.PowerShell.Commands.Internal.Format.PageHeaderEntry, Sy... shapeInfo Property
Microsoft.PowerShell.Commands.Internal.Format.ShapeInfo, System.M...

This is not what you want to search for an username.

If you do this

$allusers= get-aduser -Filter {Enabled -eq $true}
$allusers | gm

You will get an objects collection with all your properties. And here you can check with an if like that

$allusers.SamAccountName -eq "joeq"

Now you can continue you script, and to loop an all the users list, try this:

get-aduser -Filter {Enabled -eq $true} | Foreach {
  if ($_.SamAccountName -eq "something") { "OK" }
}
3

You can use "select -expand xxx" to produced a flattened(?) array where you don't need to use the property name for parsing the array.

$allusers = get-aduser -Filter {Enabled -eq $true} | select -expand samAccountName
PS C:\> $allusers.contains("GeoSword")
True
2

If you don't need to use $allusers as a collection of user objects multiple times (or if all you need is the names of users,) you can cut down on what you're storing in the shell's memory by doing this:

$usernames = (get-aduser -Filter {Enabled -eq $true}).samAccountName
$usernames.Contains("check_value")

Or say you had an entire array of check values you wanted to loop through named $check_array and you wanted to check every username to see if they were in that array :

$usernames = (get-aduser -Filter {Enabled -eq $true}).samAccountName
foreach ($i in $check_array) {
    $usernames.Contains($i)
}

Although, depending on the relative lengths of the two arrays, that second one might go better as:

foreach ($j in (get-aduser -Filter {Enabled -eq $true}).samAccountName) {
    $check_array.Contains($j)
}

Finally, if you don't need to reuse the list of usernames after this step, and you only need to check it for one check value:

((get-aduser -Filter {Enabled -eq $true}).samAccountName).Contains("check_value")

Will return the boolean result of that check.

That last method, however, is not exactly best practices if you are writing a script or function that someone else may need to take apart to repair or repurpose in your absence. If it's just for you and all you need is that one boolean result, though, I'd go with that one, it should run about the same amount of time, and you won't be holding on to that (potentially) huge array of usernames (or even larger array of objects AND the array of user names.) Again, though, if you're reusing that array of user objects, your way should be fine.

5
  • I think the syntax ($somearray).samAccountName for taking a property out of array members and making a new array, is only in PowerShell 3 and newer, it doesn't seem to work in v2. Oct 23, 2014 at 18:24
  • The last method you may as well -Filter {Enabled -eq $true -and sAMAccountName -eq "check_value"} directly. Oct 23, 2014 at 18:26
  • Using -Filter {Enabled -eq $true -and sAMAccountName -eq "check_value"} would return either a single object in the case of a unique sAMAccountName or a collection of objects. I structured it that way so that it would return a boolean value, because in the working solution he gave it was returning a boolean value. That's true about v2, though. I only upgraded like a month ago and I totally forgot that was one of the changes. I had to double check on my server environment. Oct 23, 2014 at 19:33
  • Also, that was an excellent suggestion to use | get-member that's my jam, right there. Oct 23, 2014 at 19:37
  • I didn't think of that, but in the case of if (get-stuff) {} an empty result from get-stuff will act like boolean false, and single or multiple results act like boolean true, so it would probably work. cough aftermarket excuses cough. (And sAMAccountName will be unique on any single domain setup). Oct 23, 2014 at 22:18
1

Counting through the array with ($i=0; $i ..., could be so much neater - PowerShell iterates through things quite nicely with foreach, but often you don't need that either. Your final code:

$allusers= get-aduser -Filter {Enabled -eq $true}
$usernames = @()
for ($i=0; $i -le $allusers.length; $i++) {
    $usernames += $allusers[$i].SamAccountName
}
$usernames -contains "joeq"

could be:

$usernames = Get-ADUser -Filter {Enabled -eq $true} | select -Expand sAmAccountName
$usernames -contains "joeq"

Where the select is choosing the property or properties to keep, and the expand parameter flattens this one property into a plain string instead of keeping it as a name/value pair.

[This comes to the same answer as Craig620 but I'm answering against the counting for-loop so the path to the answer is different].

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .