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.
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:
Okay, I got mine to finally work using the following syntax and using the following example from up above:
Previously:
Working Version:
I had to add $($ ) to $SamAc before PowerShell could access the variable string value.
Hopefully, this helps someone!
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.Get-Help about_ActiveDirectory_Filter
).ALWAYS use a string to construct a
-Filter
-parameter argument, because the parameter's actual type is[string]
Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"
Get-Help about_ActiveDirectory_Filter
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 theGet-ADUser
cmdlet, because the-Filter
parameter is of type[string]
- across all cmdlets that support this parameters; verify withGet-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.Get-ADUser
, that domain-specific language (query language) is documented inGet-Help about_ActiveDirectory_Filter
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:
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
containsjdoe
for instance, the commands above pass one of the following (equivalent) literals to-Filter
: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:
'...'
)$date
), not expressions ($date.Year
or$($date.Year)
); e.g.: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:Therefore, it is literal
sAMAccountName -eq "$SamAc"
that is passed toGet-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
:I.e.,
Get-ADUser
does its own, Powershell-like interpretation of the string literalsAMAccountName -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:
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:
Get-Help about_ActiveDirectory_Filter
commendably only discusses strings,it is
Get-Help Get-ADUser
that, regrettably, uses script blocks for all its examples.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:
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?
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.
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.
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.