1

I am working on a Powershell script (with a GUI) to help my colleagues easier find redundant and disabled AD accounts.

Here is a little preview...

$props = "Name, Enabled, PasswordExpired, Company,passwordNeverExpires, Office"

$propsAsArray = $props -split ',\s*'

Get-ADUser -filter * -properties $propsAsArray | where {$_.Enabled -eq $true} | where {$_.PasswordNeverExpires -eq $false}| where {$_.passwordexpired -eq $false} | Select-Object $propsAsArray | Export-csv -Path "C:\report.csv"

This all works fine and outputs a CSV report.

The snag though is how to assign all the possible combinations and permutations of AD account status to a variable and later substitute in the variable into the Get-ADUser cmdlet (depending on which radio button the user clicks in the GUI).

I've tried all can think of but only ever get back the error Expressions are only allowed as the first element of a pipeline.

I'm sure that $accountStatus = "where {$_.Enabled -eq $true} | where {$_.PasswordNeverExpires -eq $false}" (or subtle variants) are not how it is done.

I'm relatively new to Powershell and am eager to get experience. Thanks, William.

William Lombard
  • 337
  • 1
  • 3
  • 14
  • 2
    It is actually not the best idea to query All users from your AD each time you run such a query and filter them afterwards with several `Where-Object`s. Better would be to create the appropriate filter strings to use with the paramter `-Filter` as this puts less stress to your AD. ;-) – Olaf Mar 14 '21 at 17:52
  • Thanks for tip. The final version of the script will use the 'SearchBase' parameter to reduce the load on the DC(s). I'd gladly the -Filter parameter to find users whose passwords have expired but it looks like that is not possible. My research suggests that 'PasswordExpired' is a method and not a standard property and thus cannot be filtered. The 'chained' where statements are the only workaround that seems to work. – William Lombard Mar 14 '21 at 17:57
  • My code is based on the final suggested solution over at https://serverfault.com/questions/723217/find-out-if-password-expired-or-when-it-expires-for-user-in-a-specific-ou. My main concern is how to get the chained 'Where-Object" statements into a variable that i can substitute into the "Get-ADUser" cmdlet. Thanks. – William Lombard Mar 14 '21 at 18:07
  • @WilliamLombard - i suspect that i am misunderstanding your Question ... but the `W-O` scriptblock can be just like any other scriptblock. put each test in ONE scriptblock with the appropriate `-and` or `-or` operators. – Lee_Dailey Mar 14 '21 at 18:08

2 Answers2

4

Note: This answer addresses the question as asked, using a generalized Where-Object-based solution based on script blocks ({ ... }), but in the case at hand a string-based solution based on Get-ADUser's -Filter parameter, which efficiently filters at the source, as shown in the second command in Thomas' answer, is preferable.


Store an array of script blocks ({ ... }) representing the conditionals in a variable, and use an array of indices to select which conditionals to apply situationally, based on the user's GUI selections:

# All individual conditions to test, expressed as an array of script blocks.
# Note: No need for `-eq $true` to test Boolean properties, and
#       `-eq $false` is better expressed via the `-not` operator.
$blocks = 
  { $_.Enabled },
  { -not $_.PasswordNeverExpires },
  { $_.PasswordExpired }


# Select the subset of conditions to apply using AND logic, using 
# an array of indices, based on the GUI selections.
$indices = 0..2   # e.g., all 3 conditions (same as: 0, 1, 2)

Get-ADUser -Filter * -properties $propsAsArray | Where-Object { 
  # The following is equivalent to combining the conditionals of interest
  # with -and (AND logic):
  foreach ($block in $blocks[$indices]) { # Loop over selected conditionals
    if (-not (& $block)) { return $false } # Test, and return $false instantly if it fails.
  }
  $true # Getting here means that all conditions were met.
}

Note how each block is executed via &, the call operator.

mklement0
  • 382,024
  • 64
  • 607
  • 775
3

You can condense your multiple Where-Object calls by concatenating each condition with and:

Get-ADUser -Filter * -Properties $propsAsArray | Where-Object {(($_.Enabled -eq $true) -and ($_.PasswordNeverExpires -eq $false)) -and ($_.passwordexpired -eq $false)} | Select-Object $propsAsArray | Export-csv -Path "C:\report.csv"

But as Olaf already pointed out in the comments, it is even better to already use the -Filter parameter of Get-ADUser. There, you can use a similar combination of your conditions:

Get-ADUser -Filter {((Enabled -eq $true) -and (PasswordNeverExpires -eq $true)) -and (passwordexpired -eq $false)} -Properties $propsAsArray | Select-Object $propsAsArray | Export-csv -Path "C:\report.csv"
stackprotector
  • 10,498
  • 4
  • 35
  • 64
  • Thanks, but how can I assign the chained "Where-Object" statements to a variable that can then later be substituted back into "Get-ADUser" cmdlet (to then produce a report)?. There are going to many variants depending on whether or not the account is still enabled but the password has expired etc etc. – William Lombard Mar 14 '21 at 18:14
  • Thanks, I've just given that a very quick try and it's not throw back any errors. Legend! :-) – William Lombard Mar 14 '21 at 18:25
  • 1
    You can store each radio button setting in a variable and insert them instead of `$true` or `$false`. E. g.: `((Enabled -eq $rbEnabled) -and (PasswordNeverExpires -eq $rbPasswordNeverExpires)) -and (passwordexpired -eq $rbpasswordexpired)` – stackprotector Mar 14 '21 at 18:28
  • 2
    Nice, but as an aside: While seductively convenient, the [use of script blocks (`{ ... }`) as `-Filter` arguments](https://stackoverflow.com/a/44184818/45375) is conceptually problematic and can lead to misconceptions. It would work in this case, however. – mklement0 Mar 14 '21 at 18:42