Get-Aduser -Filter will not accept a variable

2018-12-31 10:01发布

I'd like to check if a user account already exists in the system.

$SamAc = Read-Host 'What is your username?'
$User = Get-ADUser -Filter {sAMAccountName -eq "$SamAc"}

I'm not sure why, but $User will always return null even if {sAMAccountName -eq "$SamAc"} is supposed to be true.

What am I missing here?

Edit:

This is what was missing:

$User = Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"

Editor's note: The script block ({ ... }) was replaced with a string.

6条回答
弹指情弦暗扣
2楼-- · 2018-12-31 10:13
if (($ADUser = Get-ADUser -filter "SamAccountName -eq '$(Read-Host Username)'") -ne $null) {$ADUser.SamAccountName} else {"Not Found"}
查看更多
与君花间醉酒
3楼-- · 2018-12-31 10:15

This one bit me when I first started to work with the ActiveDirectory module, and it was a pain to figure out.

The -Filter parameter for the ActiveDirectory module cmdlets is actually looking for a string. When you do {sAMAccountName -eq "$SamAc"} as the value, it is actually looking for "sAMAccountName -eq ""`$SamAc"""

Basically, Powershell parses the parameter and turns its value into a string, and will not interpolate the variable. Try building the string before hand, and it should work.

Something like this:

$SamAc = Read-Host 'What is your username?'    
$filter = "sAmAccountname -eq ""$SamAc"""
$User = Get-ADUser -Filter $filter
查看更多
爱死公子算了
4楼-- · 2018-12-31 10:17

Okay, I got mine to finally work using the following syntax and using the following example from up above:

Previously:

$User = Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"

Working Version:

$user = Get-aduser -Filter "sAMAccountName -eq '$($SamAc)'"

I had to add $($ ) to $SamAc before PowerShell could access the variable string value.

Hopefully, this helps someone!

查看更多
长期被迫恋爱
5楼-- · 2018-12-31 10:21

There is valuable information in the existing answers, but I think a more focused summary is helpful:

tl;dr

  • NEVER use a script block - { ... } - to construct a -Filter-parameter argument, for any cmdlet.

    • It works only in very limited circumstances - see below.
    • It gives the mistaken impression that the filter is a piece of PowerShell code, which it isn't (the Active Directory provider supports only a few, PowerShell-like operators, but their behavior partly differs from that of their PowerShell counterparts - see Get-Help about_ActiveDirectory_Filter).
  • ALWAYS use a string to construct a -Filter-parameter argument, because the parameter's actual type is [string]


The correct and robust approach is to construct a string

  • Any argument you pass to -Filter is coerced to a string first anyway, before it is passed to the Get-ADUser cmdlet, because the -Filter parameter is of type [string] - across all cmdlets that support this parameters; verify with Get-ADUser -?

  • With -Filter in general, it is up to the cmdlet to interpret that string, using a domain-specific language that often has little in common with PowerShell.

Use PowerShell's string interpolation (or string concatenation from literals and variable references / expressions) to "bake" any variable references into the string, up front.

For instance, the following commands all work and are functionally equivalent, using various PowerShell techniques to construct the string, with variations in quoting styles and escape character:

# All these commands are equivalent.
Get-ADUser -Filter "sAMAccountName -eq ""$SamAc"""
Get-ADUser -Filter "sAMAccountName -eq `"$SamAc`""                                    #`
Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"
Get-ADUser -Filter ('sAMAccountName -eq "' + $SamAc + '"')

The important part is to ensure that $SamAc gets expanded up front (replaced by its value), either via a double-quoted string "..." or by explicit string concatenation (+ $SamAc + ...).

If $SamAc contains jdoe for instance, the commands above pass one of the following (equivalent) literals to -Filter:

sAMAccountName -eq "jdoe"
sAMAccountName -eq 'jdoe'

However, with data types other than numbers and strings, you may still need to use the AD provider's variable evaluation (see below):

For instance, a stringified [datetime] value (e.g., 08/15/2018 12:45:58) may not be recognized as a date by the AD provider[1].

In that event:

  • Use a single-quoted string ('...')
  • Be sure to use only simple variable references (e.g., $date), not expressions ($date.Year or $($date.Year)); e.g.:
# Note the use of '...' and be sure to use just a variable reference, 
# not an expression.
# With this approach, never use embedded quoting, even with string variables.
Get-ADUser -Filter 'whenChanged -ge $date' 

Caveat: This approach won't work with implicitly remoting modules, because the variable reference is then evaluated on the remote machine.


If you specify a script block - which you should avoid:

A script block, when converted to a string, results in the literal contents between { and } - no variable expansion (interpolation) takes place:

 PS> {sAMAccountName -eq "$SamAc"}.ToString()
 sAMAccountName -eq "$SamAc"  # !! $SamAc was *not* expanded

Therefore, it is literal sAMAccountName -eq "$SamAc" that is passed to Get-ADUser.

Get-ADUser, perhaps in an effort to support the script-block syntax / be more PowerShell-like, does support referencing a variable unquoted - note the absence of " around $SamAc:

{ sAMAccountName -eq $SamAc }  # as stated, Get-ADUser doesn't see the { and }

I.e., Get-ADUser does its own, Powershell-like interpretation of the string literal sAMAccountName -eq $SamAc: just like you don't have to enclose a variable reference in "..." in a PowerShell expression (e.g., 'Windows_NT' -eq $env:OS), you don't have to here either - and actually must not, as evidenced by the OP's attempt failing.

However, this emulation of a regular PowerShell script block is not only confusing - because users still think they need enclosing quotes - but also half-baked, and therefore brittle:

  • It doesn't work with property references or method calls:

    { sAMAccountName -eq $searchObj.SamAccountName } # !! DOES NOT WORK
    
  • It doesn't work with implicitly remoting modules, because the variable reference is evaluated on the remote machine, looking for the variable there.


The source of the script-block confusion

While:

With one exception, the examples use only literals inside the script blocks, where the problem never surfaces. That one exception (as of this writing) is:

-filter { lastLogon -le $logonDate }

That example happens to work - it uses a simple variable reference without quotes - but, due to choosing a non-string value, no one is tempted to mistakenly apply enclosing quotes in this case - unlike when comparing string fields.

The script-block syntax is seductive and convenient, because quoting becomes easier: you don't need nested quoting.
However, for all the reasons discussed it should be avoided.


[1] If someone could verify this, it would be great (I personally can't): The string-expansion-up-front equivalent of variable-evaluated-by-AD-provider statement
Get-ADUser -Filter 'whenChanged -ge $date'
is
Get-ADUser -Filter "whenChanged -ge '$date'"
which means that the AD provider ultimately sees something like
whenChanged -ge '01/15/2018 16:00:00'
as the filter expression; for this to work, it would have to (a) accept a string as the operand, and (b) understand PowerShell's date/time format, which is the invariant culture's format (US-like dates with month listed first, but 24-hour clock).
Does it?

查看更多
其实,你不懂
6楼-- · 2018-12-31 10:25

I have to comment on this because it really aggravated me to sort this out.

Joseph Alcorn has the right idea. The filter parameter takes a string and then evaluates that in order to process the filter. What trips people up with this is that you are given the option to use curly brackets instead {}, and this doesn't work as you'd expect if you were using Where... it still has to be treated like a string.

$SamAc = Read-Host 'What is your username?'
$User = Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"

I recommend sticking to quotes to make it more clear/readable for yourself and others and to avoid potential syntax errors, or stick to Where{} in the pipeline. When doing so, I find it best to use double-quotes on the outside & single-quotes on the inside so you still get intellisense detection on the variable.

查看更多
姐姐魅力值爆表
7楼-- · 2018-12-31 10:36

Simply remove the quotes around your variable:

$SamAc = Read-Host 'What is your username?'

$User = Get-ADUser -Filter {sAMAccountName -eq $SamAc}

This should work just fine.

查看更多
登录 后发表回答