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

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

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

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

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

Edit:

Это то, чего не хватало:

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

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

Ответ 1

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

ТЛ; др

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

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


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

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

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

Используйте строковую интерполяцию 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
    
  • Он не работает с неявно удаленными модулями, потому что ссылка на переменную оценивается на удаленном компьютере и ищет там переменную.


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

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

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

-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 действительно не распознает этот формат.

Ответ 2

Этот бит мне, когда я впервые начал работать с модулем 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

Ответ 3

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

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

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

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

Ответ 4

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

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

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

Это должно работать нормально.

Ответ 5

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

Ответ 6

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

Ранее:

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

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

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

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

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