Get-Aduser -Filter не будет принимать переменную

Я хотел бы проверить, существует ли учетная запись пользователя в системе.

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

Я не уверен почему, но $User всегда будет возвращать ноль, даже если {sAMAccountName -eq "$SamAc"} должно быть правдой.

Что мне здесь не хватает?

Редактировать:

Вот чего не хватало:

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

Примечание редактора: блок скрипта ( { ... } ) был заменен строкой.

8 ответов

В существующих ответах содержится ценная информация, но я думаю, что более целенаправленное резюме полезно:

ТЛ; др

  • НИКОГДА не используйте блок скрипта - { ... } - построить -Filter -параметр аргумента, для любого командлета.

    • Это работает только в очень ограниченных обстоятельствах - см. Ниже.
    • Создается ошибочное впечатление, что фильтр является частью кода PowerShell, а это не так (он поддерживает только несколько операторов, чье поведение частично отличается от их аналогов в PowerShell - см. Get-Help about_ActiveDirectory_Filter).
  • ВСЕГДА используйте строку для построения -Filter -параметр аргумента, потому что фактический тип параметра [string]

    • В данном случае: Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"
    • Увидеть Get-Help about_ActiveDirectory_Filter

Правильный и надежный подход заключается в создании строки

  • Любой аргумент, который вы передаете -Filter в любом случае сначала приводится к строке, прежде чем она передается Get-ADUser командлет, потому что -Filter параметр имеет тип [string] - по всем командлетам, которые поддерживают эти параметры; проверить с Get-ADUser -?

  • С -Filter в общем случае командлет должен интерпретировать эту строку, используя язык, специфичный для домена, который часто имеет мало общего с PowerShell.

    • В случае Get-ADUser этот предметно-ориентированный язык (язык запросов) задокументирован в Get-Help about_ActiveDirectory_Filter

Используйте строковую интерполяцию PowerShell (или конкатенацию строк из литералов и переменных / ссылок / выражений), чтобы "запечь" любые переменные ссылки в строке.

Например, все следующие команды работают и являются функционально эквивалентными, используя различные методы PowerShell для построения строки, с различными стилями цитирования и escape-символом:

# 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 + '"')

Важной частью является обеспечение того, чтобы $SamAc расширяется сразу (заменяется его значением) либо через строку в двойных кавычках "..." или путем явной конкатенации строк (+ $SamAc + ...).

Если $SamAc содержит jdoe например, команды выше передают один из следующих (эквивалентных) литералов -Filter:

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

Однако с типами данных, отличными от чисел и строк, вам все равно может понадобиться использовать оценку переменных поставщика AD (см. Ниже):

Например, строка [datetime] значение (например, 08/15/2018 12:45:58) не может быть распознан как дата провайдером AD [1].

В этом случае:

  • Используйте строку в одинарных кавычках ('...')
  • Обязательно используйте только простые ссылки на переменные (например, $date), а не выражения ($date.Year или же $($date.Year)); например:
# 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' 

Предостережение: этот подход не будет работать с неявно удаленными модулями, потому что ссылка на переменную затем оценивается на удаленной машине.


Если вы указываете блок скрипта - чего вам следует избегать:

Блок скрипта при преобразовании в строку приводит к буквальному содержанию между { а также } - нет расширения переменной (интерполяция):

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

Следовательно, это буквально sAMAccountName -eq "$SamAc" это передается Get-ADUser,

Get-ADUser Возможно, чтобы поддержать синтаксис блока скриптов / быть более похожим на PowerShell, он поддерживает ссылку на переменную без кавычек - обратите внимание на отсутствие " вокруг $SamAc:

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

То есть, Get-ADUser делает свою собственную, похожую на Powershell интерпретацию строкового литерала sAMAccountName -eq $SamAc: так же, как вам не нужно заключать ссылку на переменную в "..." в выражении PowerShell (например, 'Windows_NT' -eq $env:OS), вы также не должны здесь - и на самом деле не должны, о чем свидетельствует попытка ОП провалиться.

Однако эта эмуляция обычного блока сценариев PowerShell не только сбивает с толку - потому что пользователи все еще думают, что им нужно заключить в кавычки - но также и наполовину испечена, и поэтому хрупкая:

  • Он не работает со ссылками на свойства или вызовами методов:

    { sAMAccountName -eq $searchObj.SamAccountName } # !! DOES NOT WORK
    
  • Он не работает с неявно удаленными модулями, потому что ссылка на переменную оценивается на удаленном компьютере и ищет там переменную.


Источник скрипта-блока путаницы

В то время как:

  • Get-Help about_ActiveDirectory_Filter похвально только обсуждает строки,

  • это Get-Help Get-ADUser К сожалению, он использует блоки скриптов для всех своих примеров.

За одним исключением, примеры используют только литералы внутри блоков скрипта, где проблема никогда не появляется. Это одно исключение (на момент написания статьи):

-filter { lastLogon -le $logonDate }

Этот пример работает - он использует простую ссылку на переменную без кавычек - но из-за выбора нестрокового значения никто не испытывает соблазна ошибочно применить заключающие в кавычки в этом случае - в отличие от сравнения строковых полей.

Синтаксис блока скриптов является соблазнительным и удобным, потому что цитирование становится проще: вам не нужно вложенное цитирование.
Однако по всем обсуждаемым причинам этого следует избегать.


[1] Если бы кто-то мог проверить это, было бы замечательно (лично я не могу): прямое расширение строки, эквивалентное выражению переменной-оценено-AD-провайдера
Get-ADUser -Filter 'whenChanged -ge $date'
является
Get-ADUser -Filter "whenChanged -ge '$date'"
Это означает, что провайдер AD в конечном итоге видит что-то вроде
whenChanged -ge '01/15/2018 16:00:00'
как выражение фильтра; чтобы это работало, он должен (а) принять строку в качестве операнда и (б) понять формат даты / времени PowerShell, который является форматом инвариантной культуры (даты, подобные США, с указанием месяца в начале, но 24 часы)
Является ли?

Это меня немного поразило, когда я впервые начал работать с модулем ActiveDirectory, и это было сложно понять.

-Filter Параметр для командлетов модуля ActiveDirectory фактически ищет строку. Когда вы делаете {sAMAccountName -eq "$SamAc"} в качестве значения, он на самом деле ищет "sAMAccountName -eq ""`$SamAc"""

По сути, Powershell анализирует параметр и превращает его значение в строку и не будет интерполировать переменную. Попробуйте построить строку перед рукой, и она должна работать.

Что-то вроде этого:

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

Я должен прокомментировать это, потому что это действительно раздражало меня, чтобы разобраться в этом.

У Джозефа Алкорна правильная идея. Параметр фильтра принимает строку, а затем оценивает ее для обработки фильтра. Что удивляет людей, так это то, что вам предоставляется возможность использовать фигурные скобки вместо {}, и это не сработает, как вы ожидаете, если бы вы использовали Where... это все равно должно рассматриваться как строка.

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

Я рекомендую придерживаться кавычек, чтобы сделать их более понятными / читабельными для себя и других и избежать возможных синтаксических ошибок, или придерживаться Where{} в конвейере. При этом я считаю, что лучше всего использовать двойные кавычки снаружи и одинарные кавычки внутри, чтобы вы все равно получали определение intellisense для переменной.

Просто удалите кавычки вокруг вашей переменной:

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

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

Это должно работать просто отлично.

if (($ADUser = Get-ADUser -filter "SamAccountName -eq '$(Read-Host Username)'") -ne $null) {$ADUser.SamAccountName} else {"Not Found"}

Мне потребовалось совсем немного, чтобы просто использовать

Не заключайте в кавычки ссылку на переменную ("$SamAc").

ТХ так много

Хорошо, я наконец-то получил работу, используя следующий синтаксис и приведенный ниже пример сверху:

Ранее:

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

Рабочая версия:

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

Мне пришлось добавить $($) в $SamAc, прежде чем PowerShell смог получить доступ к значению переменной строки.

Надеюсь, это кому-нибудь поможет!

Небольшое дополнение, если кто-то вроде меня попал сюда и все еще рвал себе волосы:

-properties *  

Было бы довольно часто использовать это в этом запросе. Не работает, я уверен, что кто-то умнее меня сможет это понять

-свойства mail,cn,wtf

и т.д. работает как ожидалось

Другие вопросы по тегам