Get-Aduser -Filter will not accept a variable
Asked Answered
A

8

30

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.

Ammonium answered 19/11, 2013 at 15:27 Comment(3)
Lose the "" around the variable.Nissy
Similar question: #16402694Outcrop
Another similar question: #12727888Outcrop
M
65

There is valuable information in the existing answers, but I think a more focused summary is helpful. Note that the original form of this answer advocated strict avoidance of script blocks ({...}) and AD-provider variable evaluation, but this has been replaced with more nuanced recommendations.

Option A: Letting the AD provider resolve - stand-alone only - variable references:

Get-ADUser -Filter 'sAMAccountName -eq $SamAc'  # note the '...' quoting
  • Note the use of '...', i.e. a verbatim (single-quoted) string, because the string's value is to be passed as-is to the AD provider (cmdlet).

    • While use of a script block ({ ... }),
      Get-ADUser -Filter { sAMAccountName -eq $SamAc }, technically works too (its verbatim content, sans { and }, is converted to a string), it is conceptually problematic - see bottom section.
  • Do not quote the variable reference ("$SamAc").

  • Use only stand-alone variable references (e.g, $SamAc); expressions, notably including property access are not supported (e.g., not $user.SamAccountName or "$name*" or $("admin_" + $SamAc)); if necessary, use an intermediate, auxiliary variable; e.g.:

    • $sam = $user.SamAccountName; Get-ADUser -Filter 'sAMAccountName -eq $sam'
  • Generally, only a subset of PowerShell's operators are supported, and even those that are do not always behave the same way - see bottom section.

  • Caveat: If you use Get-ADUser via an implicitly remoting module - whether self-created via Import-PSSession or, in PowerShell v7+, via the Windows Compatibility feature - neither '...' nor { ... } works, because the variable references are then evaluated on the remote machine, looking for the variables there (in vain); if (Get-Command Get-ADUser).CommandType returns Function, you're using an implicitly remoting module.

    • If implicit remoting is involved, you MUST use string interpolation, as shown next.

Option B: Using PowerShell's string interpolation (expandable strings), up front:

Get-ADUser -Filter "sAMAccountName -eq `"$SamAc`"" # note the "..." quoting
  • Using "...", i.e. an expandable (double-quoted) string makes PowerShell interpolate (expand) all variable references and subexpression up front, in which case the AD provider sees only the (variable-free) result.

  • As shown above, for string operands embedded quoting then is necessary.

    • For embedded quoting, '...' is a simpler alternative to `"...`" (`" is an escaped ") - e.g. "sAMAccountName -eq '$SamAc'" - but note that this assumes that an expanded value doesn't itself contain ', which is a distinct possibility with last names, for instance.
  • Also, be sure to `-escape constants such as $true, $false, and $null inside the "..." string, which are always recognized by the AD provider; i.e., use `$true, `$false and `$null, so that PowerShell doesn't expand them up front.

  • Caveat: Using an expandable string does not work with all data types, at least not directly: for instance, the default stringification of a [datetime] instance (e.g., 01/15/2018 16:00:00 is not recognized by the AD provider; in this case, embedding the result of a call to the instance's .ToFileTime() (or .ToFileTimeUtc()?) method into the string may be necessary (as suggested in the comments on this post); I'm unclear on whether there are other data types that require similar workarounds.

  • On the plus side, string interpolation allows you to embed entire expressions and even commands in a "..." string, using $(...), the subexpression operator; e.g.:

    # Property access.
    Get-ADUser -Filter "sAMAccountName -eq `"$($user.SamAccountName)`""
    # String concatenation
    Get-ADUser -Filter "sAMAccountName -eq `"$('admin_' + $SamAc)`""
    

Background

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

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

    • In the case of Get-ADUser, that domain-specific language (query language) is documented in Get-Help about_ActiveDirectory_Filter.

      • Note: As of this writing, no newer version of this legacy topic exists; this GitHub issue requests one.
    • With Get-AdUser, the language supported by -Filter is certainly modeled on PowerShell, but it has many limitations and some behavioral differences that one must be aware of, notably:

      • As Santiago Squarzon points out, these limitations and difference stem from the fact that the language is translated into an LDAP filter behind the scenes, it is therefore constrained by its features and behaviors. (Note that you can use the -LDAPFilter parameter in lieu of -Filter to directly pass an LDAP filter).

      • Only a limited subset of PowerShell operators are supported, and some exhibit different behavior; here's a non-exhaustive list:

        • -like / -notlike only support * in wildcard expressions (not also ? and character sets/ranges ([...])
          • '*' by itself represents any nonempty value (unlike in PowerShell's wildcard expressions, where it also matches an empty one).
          • Instead of -eq "" or -eq $null to test fields for being empty, use
            -notlike '*'.
          • Certain AD fields, e.g., DistinguishedName, only support '*' by itself, not as part of a larger pattern; that is, they only support an emptiness test.
        • There is no support for regex matching.
        • -lt / -le and -gt / -ge only perform lexical comparison.
        • Referencing a nonexistent / misspelled property name causes the Get-ADUser command to quietly return $null.
      • As stated, only stand-alone variable references are supported (e.g, $SamAc), not also expressions (e.g., $SamAc.Name or $("admin_" + $SamAc))

  • While you can use a script block ({ ... }) to pass what becomes a string to -Filter, and while this syntax can be convenient for embedding quotes, it is problematic for two reasons:

    • It may mislead you to think that you're passing a piece of PowerShell code; notably, you may be tempted to use unsupported operators and expressions rather than simple variable references.

    • It creates unnecessary work (though that is unlikely to matter in practice), because you're forcing PowerShell to parse the filter as PowerShell code first, only to have the result converted back to a string when the argument is bound to -Filter.

Maquette answered 25/5, 2017 at 15:56 Comment(2)
Nice answer mklement0, I think it could be worth noting in the comparison operators section of the answer that the reason why certain operators as well as wildcard expressions (supporting only *) are not supported is because they're not supported by LDAP and that the provider, behind the scenes, when we feed a filter expression to the -Filter parameter has to parse and translate that expression into LDAP Syntax before performing the AD Query.Elielia
Thank you, @Santiago, please see my update, which includes this information. I've also restructured the answer to hopefully be conceptually clearer. As stated, it would be great to get clarity on the .ToFileTime() vs. .ToFileTimeUtc() issue, (and whether any other data types require similar special treatment when string interpolation is used).Maquette
B
13

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
Berndt answered 19/11, 2013 at 19:13 Comment(2)
This was of great help for me, I finally used something like: Get-ADObject -Properties mail, proxyAddresses -Filter "mail -eq ""$Mail"" -or proxyAddresses -eq ""smtp:$Mail"""Regularly
Good advice to use strings rather than script blocks, but Get-ADUser (surprisingly) does interpret variable references, except that it doesn't expect enclosing literal quotes in that case.Maquette
N
4

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.

Nautch answered 4/12, 2015 at 18:57 Comment(0)
T
1

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.

Tallyho answered 1/12, 2015 at 18:30 Comment(0)
L
0
if (($ADUser = Get-ADUser -filter "SamAccountName -eq '$(Read-Host Username)'") -ne $null) {$ADUser.SamAccountName} else {"Not Found"}
Lacefield answered 14/7, 2016 at 3:47 Comment(2)
How is your answer better than the existing ones? You even didn't include any description for your code why it works (or why it works better than the existing answers).Masked
While this code snippet may solve the question, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.Mayhap
K
-1

Little addendum if anyone like me got here and was still tearing their hair out:

-properties *  

Would be quite a common this to have in this query. Doesn't work, I'm sure someone smarter than me can figure it out

-properties mail,cn,wtf

etc does work as expected

Kayleigh answered 25/10, 2020 at 14:39 Comment(0)
J
-1

It took me quite a bit to just use

Do not quote the variable reference ("$SamAc").

TXH so much

Jessamyn answered 28/7, 2022 at 9:32 Comment(1)
Asjer has a problem with not being able to use a variable with a powershell LDAP query command. So state that the Asker should just not try to do what it is that the Asker wants to do is not helping the script to complete. If Your answer is perhaps it is not possible to use a variable ... perhaps it's just an English translation thing? As it stands your answer is analogous to how can i jump the rope while chewing gum, answered with do not chew gum. :) Consider adding more words to clarify what you mean.Casebound
P
-2

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.

Positronium answered 30/3, 2017 at 1:19 Comment(1)
These two interpolating strings ("...") result in the exact same string literal getting passed to Get-ADUser, so the 1st one should work just as well as the 2nd one, which you can verify by running the following in a PowerShell console window: $SamAc = 'jdoe'; "sAMAccountName -eq '$SamAc'"; "sAMAccountName -eq '$($SamAc)'".Maquette

© 2022 - 2024 — McMap. All rights reserved.