To Scriptblock or Not to Scriptblock, That is the Question

I was doing some work with the Active Directory Powershell cmdlets recently.  Well, I work with them almost every day, but they still get me with their idiosyncrasies from time to time.

I needed to check some group memberships on various privileged groups within the directory.  I'll show you an abridged version of the code I started with to get the point across, the idea of which is that I iterate through a collection of groups (a string array) and perform some actions on each of the Active Directory groups in sequence:

Foreach($ADGroupName In [String[]]'Domain Admins',     `
                                  'Enterprise Admins', `
                                  'Administrators',    `
                                  'Account Operators', `
                                  'Backup Operators')
{
    $Group = Get-ADGroup -Filter { Name -EQ $ADGroupName } -Properties Members
    If ($Group -EQ $Null -OR $Group.PropertyNames -NotContains 'Members')
    {
        # This condition only occurs on the first iteration of the Foreach loop!
        Write-Error "$ADGroupName was null or was missing the Members property!"
    }
    Else
    {
        Write-Host "$ADGroupName contains $($Group.Members.Count) members." 
    }
}

Before I continue, I'd just like to mention that I typically do not mind very long lines of code nor do I avoid verbose variable names, mostly because I'm always using a widescreen monitor these days.  Long gone are the days of the 80-character-wide terminal.  And I agree with Don Jones that backticks (the Powershell escape character) are to be avoided on aesthetic grounds, but for sharing code in formats that are less conducive to long lines of code, such as this blog or a StackExchange site with their skinny content columns, I'll throw some backticks in to keep the horizontal scrolling to a minimum. 

Anyway, the above code exhibited the strangest bug. At least I'd call it a bug. (I'll make sure and let Jeffrey Snover know next time I see him. ;P) Only on the first iteration of the Foreach loop, I would get the "error" condition instead of the expected "Domain Admins contains 5 members" output.  The remaining iterations all behaved as expected.  It did not matter in what order I rearranged the list of AD groups; I always got an error on the first element in the array.

For a moment, I settled on working around the "bug" by making a "Dummy Group," including that as the first item in the array, gracefully handling the expected exception because Dummy Group did not exist, and then continuing normally with the rest of the legitimate groups.  This worked fine, but it didn't sit well with me.  Not exactly my idea of production-quality code.  I wanted to find the root cause of this strange behavior.

Stackoverflow has made me lazy.  Apparently I go to Serverfault when I want to answer questions, and Stackoverflow when I want other people to answer questions for me.  Simply changing line 7 above to this:

$Group = Get-ADGroup -Filter "Name -EQ '$ADGroupName'" -Properties Members

Made all the difference.  That is, using a string with an expandable variable inside it instead of the script block for a filter.  (Which itself is a little confusing since single quotes (') usually indicate non-expandable variables.  Oh well.  Just syntax to remember when playing with these cmdlets.

Nevertheless, if the code would not work correctly with a script block, I wish the parser would mark it as a syntax error, instead of acting weird.  (Behavior exists in PS 2 and PS 4, though in PS 4 the missing property is just ignored and I get 0 members, which is even worse.)

Comments (5) -

Is there some reason why you don't just do  `Get-ADGroup $ADGroupName -Properties Members`, why do you have a filter at all?

Sure that would do fine, but my work with #1 Powershell workflows, #2 The Scripting Games, and to some extent #3 Set-StrictMode, has turned me off of positional parameters for all but the most ad-hoc tasks.  Sometimes having so many ways to accomplish the same thing can be a detriment. Kind of like I mentioned in this post I tend to be very verbose and explicit with my Powershell... *ironically,* to avoid just these types of anomalies.

But at the end of the day you're right - I didn't need to use a filter. But I did and then it acted weird, so I had to end up figuring out why it was acting weird. Smile

I can get not using positional parameters in a script.  I certainly don't have them all memorized.  So being more verbose in a script is great.

For get-adgroup, the parameter for position 1 is `-Identity`, which will find a group that matches one of the following attributes [Distinguished Name, GUID (objectGUID), Security Identifier (objectSid), Security Accounts Manager (SAM) Account Name (sAMAccountName)].

So `Get-ADGroup -Identity $ADGroupName -Properties Members`

Yep. You're right.  I think your syntax is better and it's what I should have started with.  However... that doesn't answer why the scriptblock didn't work as intended.  If the scriptblock method was intended to work for all members of an array except for the first element, then why doesn't Powershell just mark it as a syntax error?  Or say something stupid like, "OK, I'll work fine for every element of this input except the first one!"

Comments are closed