none
Get-ADUser Filter that's entirely variables RRS feed

  • Question

  • I'm working on a script to automatically update security groups based off user properties. The first step is to get a list of all users that match a basic filter that changes from group to group. As an example, I want to get a list of all users who's Department property is set to "Accounting". This is what I initially wrote:

    Import-Module ActiveDirectory Function SyncSecGroup($ADProperty, $ADPValue) { $CurrentList = Get-ADUser -Filter {$ADProperty -eq $ADPValue} } SyncSecGRoup("Department","Accounting")

    I've found a lot of similar questions but I can't get any of those solutions (like using quotes or double-quotes) to work.
    Saturday, November 22, 2014 2:01 AM

Answers

  • Thank you everyone for your input; I didn't mean to start off such a chain reaction. I'm going with this syntax:

    Import-Module ActiveDirectory
    
    Function SyncSecGroup($ADProperty, $ADPValue) {
        $CurrentList = Get-ADUser -Filter {$ADProperty -eq $ADPValue}
        
        
        }
    
    SyncSecGRoup -ADProperty "Department" -ADPValue "Accounting"

    Although there are clearly multiple ways to skin this cat.  As for the question, the reason I'm writing this as a function is because I intend to call it multiple times.  My end goal is to have security groups automatically updated based off properties of the user objected.  For example, groups like "sec-Accounting" and "sec-Sales" would be automatically updated by user's department property.  Clearly this is just the very beginning of the script but that's why I'm writing it as a function.

    Again, thank you all for the very, very much for the detailed input.


    Tuesday, November 25, 2014 1:50 AM

All replies

  • Try this:

    Function SyncSecGroup($ADProperty, $ADPValue) {
        $filter = [scriptblock]::create("$ADProperty -eq '$ADPValue'")
        $CurrentList = Get-ADUser -Filter $filter
        }


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "


    Saturday, November 22, 2014 2:14 AM
    Moderator
  • No luck. I get:

    Get-ADUser : Error parsing query: 'Department Accounting -eq ''' Error Message: 'syntax error' at position: '12'.

    I've noticed this earlier when I was trying every way I could think of to present the filter. Notice the ordering of the filter parameters is screwed up.  FYI, I also tried putting the single-quotes account $ADProperty with no success.

    Saturday, November 22, 2014 2:19 AM
  • You're not passing the arguments to the function correctly.

    Parameters are passed as space-separated values for positional parameters:

    SyncSecGRoup "Department" "Accounting"

    Or using -Parameter Value or -Parameter:Value if using named parameters

    SyncSecGRoup -ADProperty "Department" -ADPValue "Accounting"
    
    or
    
    SyncSecGRoup -ADProperty:"Department" -ADPValue:"Accounting"


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "



    Saturday, November 22, 2014 2:26 AM
    Moderator
  • I would use the following:

    $filter = [scriptblock]::create("{$ADProperty -eq `$ADValue}")

    Then, the $Filter will be:

    {department -eq $ADValue}

    Edit: Another way that would work:

    $filter = [scriptblock]::create("{$ADProperty -eq ""$ADValue""}")

    Then the result would be:

    {department -eq "Accounting"}


    Richard Mueller - MVP Directory Services


    Saturday, November 22, 2014 2:33 AM
    Moderator
  • I would use the following:

    $filter = [scriptblock]::create("{$ADProperty -eq `$ADValue}")

    Then, the $Filter will be:

    {department -eq $ADValue}


    Richard Mueller - MVP Directory Services

    That doesn't work.

    It would be equivalent to writing:

    Get-ADUser -filter {{Department -eq 'Accounting'}}
    It throws a parse error on the filter.


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Saturday, November 22, 2014 2:39 AM
    Moderator
  • I'm assuming the original code:

        $CurrentList = Get-ADUser -Filter $filter
    


    Richard Mueller - MVP Directory Services

    Saturday, November 22, 2014 3:10 AM
    Moderator
  • I tested that and got:

    $ADProperty = 'Company'
    $ADValue = '001'
    
    $filter = [scriptblock]::create("{$ADProperty -eq `$ADValue}")
    
    Get-ADUser -Filter $filter
    Get-ADUser : Error parsing query: '{Company -eq $ADValue}' Error Message: 'syntax error' at position: '1'.
    At line:7 char:1
    + Get-ADUser -Filter $filter
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ParserError: (:) [Get-ADUser], ADFilterParsingException
        + FullyQualifiedErrorId : Error parsing query: '{Company -eq $ADValue}' Error Message: 'syntax error' 
        at position: '1'.,Microsoft.ActiveDirectory.Management.Commands.GetADUser
     

    The solution I posted produced the expected results.

    I also tried just an expandable string, and it didn't produce any errors, but would only return one object.  I haven't figured that out yet.......


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Saturday, November 22, 2014 3:23 AM
    Moderator
  • Stupid idea caused by a need to read TFM!

    Start here: http://www.sapien.com/blog/2013/10/29/announcing-powershell-4-0-tfm/


    ¯\_(ツ)_/¯

    Saturday, November 22, 2014 4:40 AM
  • Try this:

    Function SyncSecGroup($ADProperty, $ADPValue) {
        $filter = [scriptblock]::create("$ADProperty -eq '$ADValue'")
        $CurrentList = Get-ADUser -Filter $filter
        }


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    This is useful but it should not be a scriptblock for a number of reasons.

    You also don't need to wrap it in a function.  That only adds to the confusion.

        $user=Get-ADUser -Filter "$ADProperty -eq '$ADPValue'"

    Try this:

    $ADProperty='samaccountname'
    $ADPValue='guest'
    Get-ADUser -Filter "$ADProperty -eq '$ADPValue'" 

    You will get back a collection for department:

    $ADProperty='department'
    $ADPValue='accounting'
    $accountingusers=Get-ADUser -Filter "$ADProperty -eq '$ADPValue'"

    Get-ADUser is a function-like target. It does not need to be wwrapped yet again.


    ¯\_(ツ)_/¯

    Saturday, November 22, 2014 3:40 PM
  • Try this:

    Function SyncSecGroup($ADProperty, $ADPValue) {
        $filter = [scriptblock]::create("$ADProperty -eq '$ADValue'")
        $CurrentList = Get-ADUser -Filter $filter
        }


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    This is useful but it should not be a scriptblock for a number of reasons.

    You also don't need to wrap it in a function.  That only adds to the confusion.

        $user=Get-ADUser -Filter "$ADProperty -eq '$ADPValue'"

    Try this:

    $ADProperty='samaccountname'
    $ADPValue='guest'
    Get-ADUser -Filter "$ADProperty -eq '$ADPValue'" 

    You will get back a collection for department:

    $ADProperty='department'
    $ADPValue='accounting'
    $accountingusers=Get-ADUser -Filter "$ADProperty -eq '$ADPValue'"

    Get-ADUser is a function-like target. It does not need to be wwrapped yet again.


    ¯\_(ツ)_/¯

    I agree it's rather too trivial to need to be made into a function.  

    I'd be interested in knowing the reasons it should not be a script block. 

     

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Saturday, November 22, 2014 4:00 PM
    Moderator
  • I wasn't able to test until just now, but jrv is correct. I would expect a script block to work, but it does not. Simply using both variables in the filter works fine. In fact, neither variable needs to be quoted. The following worked for me:

    Get-ADUser -Filter {$ADProperty -eq $ADValue}


    Richard Mueller - MVP Directory Services

    Saturday, November 22, 2014 4:07 PM
    Moderator
  • I wasn't able to test until just now, but jrv is correct. I would expect a script block to work, but it does not. Simply using both variables in the filter works fine. In fact, neither variable needs to be quoted. The following worked for me:

    Get-ADUser -Filter {$ADProperty -eq $ADValue}


    Richard Mueller - MVP Directory Services

    A script block does work.  It's the "script block in a script block" that doesn't.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Saturday, November 22, 2014 4:19 PM
    Moderator
  • I was confused because when I output the value of $filter in your solution, it was not enclosed in braces (or quotes). I take your word for it that the value is already a script block.

    I just tested with -LDAPFilter, and that also works with two variables. I would note that with -LDAPFilter, the cmdlet must pass the resulting string (quoted and in parentheses) to the Domain Controller. When we use the -Filter parameter, we use an extended form of the PowerShell Expression Language. I guess it is not really a string or script block. However, the filter must be parsed by the Get-ADUser cmdlet and converted into an LDAP filter (a quoted string) before passing it to the domain controller. The DC will not understand any PowerShell syntax. The vagaries of the filter are due to the code behind the Get-ADUser cmdlet (and similar AD module cmdlets). For example, I have always wondered why the following works:

    $Dept = "Sales"
    Get-ADUser -Filter 'department -eq $Dept'

    To me, PowerShell syntax dictates that $Dept should not get resolved in a single quoted string. But the code behind Get-ADUser handles it, not PowerShell. At least that's my interpretation.


    Richard Mueller - MVP Directory Services

    Saturday, November 22, 2014 4:39 PM
    Moderator
  • Richard is correct, it is not a script lock.


    ¯\_(ツ)_/¯

    Saturday, November 22, 2014 4:50 PM
  • I was confused because when I output the value of $filter in your solution, it was not enclosed in braces (or quotes). I take your word for it that the value is already a script block.


    When you output the value of a script block it always returns the enclosed text without the braces.  If you do a .gettype() on $filter it will show that it's a scriptblock.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Saturday, November 22, 2014 4:55 PM
    Moderator
  • Placing the filter inside of a scriptblock does not process the variables.  When you put quotes around it in the present scope you are processing the variables and it loos good however it doesn't get passed as a converted string.  It gets passed as a string representation of the scriptblock.  THe variables do not convert.

    PS C:\scripts> $sb={$ADProperty -eq '$ADValue'}
    PS C:\scripts> $sb
    $ADProperty -eq '$ADValue'
    PS C:\scripts> "$sb"
    $ADProperty -eq '$ADValue'
    PS C:\scripts> $sb.Invoke()
    False
    PS C:\scripts> "{$ADProperty -eq '$ADValue'}"
    {samaccountname -eq 'guest'}
    PS C:\scripts>


    ¯\_(ツ)_/¯

    Saturday, November 22, 2014 6:30 PM
  • That's why the script block was created from an expandable string.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Saturday, November 22, 2014 6:41 PM
    Moderator
  • But, as you can see, it doesn't work.

    Your exact example:

    PS C:\scripts> $ADProperty
    samaccountname
    PS C:\scripts> $ADPValue
    guest
    PS C:\scripts> $filter = [scriptblock]::create("{$ADProperty -eq ""$ADPValue""}")
    PS C:\scripts> $filter
    {samaccountname -eq "guest"}
    PS C:\scripts> Get-AdUser -Filter $filter
    Get-AdUser : Error parsing query: '{samaccountname -eq "guest"}' Error Message: 'syntax error' at position: '1'.
    At line:1 char:1
    + Get-AdUser -Filter $filter
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ParserError: (:) [Get-ADUser], ADFilterParsingException
        + FullyQualifiedErrorId : Error parsing query: '{samaccountname -eq "guest"}' Error Message: 'syntax error' at pos
       ition: '1'.,Microsoft.ActiveDirectory.Management.Commands.GetADUser
    
    PS C:\scripts> Get-AdUser -Filter "$filter"
    Get-AdUser : Error parsing query: '{samaccountname -eq "guest"}' Error Message: 'syntax error' at position: '1'.
    At line:1 char:1
    + Get-AdUser -Filter "$filter"
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ParserError: (:) [Get-ADUser], ADFilterParsingException
        + FullyQualifiedErrorId : Error parsing query: '{samaccountname -eq "guest"}' Error Message: 'syntax error' at pos
       ition: '1'.,Microsoft.ActiveDirectory.Management.Commands.GetADUser
    


    ¯\_(ツ)_/¯

    Saturday, November 22, 2014 7:14 PM
  • That's not my example.  That's the one Richard used.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Saturday, November 22, 2014 7:21 PM
    Moderator
  • Yes, when I posted yesterday I was not able to test. I verified today that what I posted earlier raised errors, even though it "looked" correct to me.

    Richard Mueller - MVP Directory Services

    Saturday, November 22, 2014 7:33 PM
    Moderator
  • That's not my example.  That's the one Richard used.

    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Yes yours works without the typo.

    You type $ADValue whenit should be $ADPValue.  Other than that is is a string and, when the block is evaluated, it should work and does.


    ¯\_(ツ)_/¯

    Saturday, November 22, 2014 7:36 PM
  • Fixed the typo.

    Thanks for catching that.


    [string](0..33|%{[char][int](46+("686552495351636652556262185355647068516270555358646562655775 0645570").substring(($_*2),2))})-replace " "

    Saturday, November 22, 2014 8:12 PM
    Moderator
  • Thank you everyone for your input; I didn't mean to start off such a chain reaction. I'm going with this syntax:

    Import-Module ActiveDirectory
    
    Function SyncSecGroup($ADProperty, $ADPValue) {
        $CurrentList = Get-ADUser -Filter {$ADProperty -eq $ADPValue}
        
        
        }
    
    SyncSecGRoup -ADProperty "Department" -ADPValue "Accounting"

    Although there are clearly multiple ways to skin this cat.  As for the question, the reason I'm writing this as a function is because I intend to call it multiple times.  My end goal is to have security groups automatically updated based off properties of the user objected.  For example, groups like "sec-Accounting" and "sec-Sales" would be automatically updated by user's department property.  Clearly this is just the very beginning of the script but that's why I'm writing it as a function.

    Again, thank you all for the very, very much for the detailed input.


    Tuesday, November 25, 2014 1:50 AM
  • If anyone out there is looking for a script to automatically maintain role security groups, I've finished my script. I'm sure parts of it could be more pretty but it works. The script contains an example configuration, be sure to modify accordingly.

    Import-Module ActiveDirectory
    
    $LogFile = "C:\Scripts\Logs\UpdateSecGroups.log"
    $MaxLines = 10000
    $DoNotChange = $True
    $nl = [Char]13 + [Char]10
    
    If (Test-Path $LogFile) { $LogFileData = Get-Content $LogFile -Tail $MaxLines }
    Else { $LogFileData = "" }
    $LogFileData += "--------------------------" + $nl + (Get-Date -Format "yyyy/MM/dd HH:mm:ss zzz") + $nl
    
    Function SyncSecGroup($ADProperty, $ADPValue, $ADGroupName, $Testing) {
        $CurrentList = Get-ADUser -Filter {$ADProperty -eq $ADPValue} -Properties SamAccountName | Sort-Object SamAccountName
        $OldList = Get-ADGroupMember -Identity $ADGroupName | Get-ADUser -Properties SamAccountName | Sort-Object SamAccountName
        $Report = ""
        If ($OldList.Length -ne 0) {
            $RemoveList = Compare-Object –referenceobject $OldList –differenceobject $CurrentList -Property SamAccountName | where{$_.SideIndicator -eq "<="}
            ForEach ($ADUser in $RemoveList | Where { $_.SamAccountName -ne $null }) {
                If ($Testing -eq $False -And $ADUser.SamAccountName -ne "") {
                    Get-ADUser -Identity $ADUser.SamAccountName | Remove-ADGroupMember -Identity $ADGroupName
                    }
                $Report += $ADGroupName + " Remove " + $ADUser.SamAccountName + $nl
                }
            }
        If ($CurrentList.Length -ne 0) {
            If ($OldList.Length -ne 0) {
                $AddList = Compare-Object –referenceobject $OldList –differenceobject $CurrentList -Property SamAccountName | where{$_.SideIndicator -eq "=>"}
                }
            Else {
                $AddList = $CurrentList
                }
            ForEach ($ADUser in $AddList | Where { $_.SamAccountName -ne $null }) {
                If ($Testing -eq $False) {
                    Get-ADUser -Identity $ADUser.SamAccountName | Add-ADGroupMember -Identity $ADGroupName
                    }
                    $Report += $ADGroupName + " Add " + $ADUser.SamAccountName + $nl
                }
            }
        Return $Report
        }
    
    $NewLogFileData = SyncSecGRoup -ADProperty "Department" -ADPValue "Accounting" -ADGroupName "SEC-Accounting" -Testing $DoNotChange
    $NewLogFileData += SyncSecGRoup -ADProperty "Department" -ADPValue "Executive" -ADGroupName "SEC-Executive" -Testing $DoNotChange
    $NewLogFileData += SyncSecGRoup -ADProperty "Department" -ADPValue "Human Resources" -ADGroupName "SEC-Human Resources" -Testing $DoNotChange
    $NewLogFileData += SyncSecGRoup -ADProperty "Department" -ADPValue "Information Technology" -ADGroupName "SEC-Information Technology" -Testing $DoNotChange
    $NewLogFileData += SyncSecGRoup -ADProperty "Department" -ADPValue "Marketing" -ADGroupName "SEC-Marketing" -Testing $DoNotChange
    $NewLogFileData += SyncSecGRoup -ADProperty "Department" -ADPValue "Sales" -ADGroupName "SEC-Sales" -Testing $DoNotChange
    $LogFileData += $NewLogFileData.Replace($nl + $nl,$nl)
    
    Out-File -FilePath $LogFile -Encoding ascii -InputObject $LogFileData

    Wednesday, December 17, 2014 10:14 PM
  • If anyone out there is looking for a script to automatically maintain role security groups, I've finished my script. I'm sure parts of it could be more pretty but it works. The script contains an example configuration, be sure to modify accordingly.



    If you have scripts to share you should put them in the repository here: https://gallery.technet.microsoft.com/scriptcenter

    ¯\_(ツ)_/¯


    • Edited by jrv Wednesday, December 17, 2014 10:41 PM
    Wednesday, December 17, 2014 10:41 PM