Ответ 1
В существующих ответах содержится ценная информация, но я думаю, что более целенаправленное резюме полезно:
ТЛ; др
-
НИКОГДА не используйте блок сценария -
{... }
- для создания-Filter
-parameter для любого командлета.- Это работает только в очень ограниченных обстоятельствах - см. Ниже.
- Создается ошибочное впечатление, что фильтр является частью кода PowerShell, а это не так (поставщик Active Directory поддерживает только несколько операторов, подобных PowerShell, но их поведение частично отличается от поведения их аналогов в PowerShell - см.
Get-Help about_ActiveDirectory_Filter
).
-
ВСЕГДА используйте строку для создания
-Filter
-parameter, потому что фактическим типом параметра является[string]
- В данном случае:
Get-ADUser -Filter "sAMAccountName -eq '$SamAc'"
- Смотрите
Get-Help about_ActiveDirectory_Filter
- В данном случае:
Правильный и надежный подход заключается в создании строки
-
Любой аргумент, который вы передаете
-Filter
все равно сначала приводится к строке, прежде чем он передается в командлетGet-ADUser
, поскольку параметр-Filter
имеет тип[string]
- во всех командлетах, которые поддерживают эти параметры; проверить с помощьюGet-ADUser -?
-
В
-Filter
с-Filter
дело до командлета, который интерпретирует эту строку, используя специфичный для домена язык, который часто имеет мало общего с PowerShell.- В случае
Get-ADUser
этот предметно-ориентированный язык (язык запросов)Get-Help about_ActiveDirectory_Filter
в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
) не распознается поставщиком данных как дата [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
есть Get-ADUser
делает свою собственную, похожую на Powershell интерпретацию строкового литерала sAMAccountName -eq $SamAc
: точно так же, как вам не нужно заключать ссылку на переменную в "..."
в выражении PowerShell (например, 'Windows_NT' -eq $env:OS
), вам здесь тоже не нужно - и на самом деле это не обязательно, о чем свидетельствует неудачная попытка OP.
Однако эта эмуляция обычного блока сценариев 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-часовой) Часы).
Хотя я не могу лично проверить это поведение, Люк сообщает, что провайдер AD действительно не распознает этот формат.