PS Remoting para AD, Get-ADUser e blocos de script com variáveis locais

1

Estou tentando escrever um script PS para gerenciar objetos do AD, conectando-me a um DC e recuperando informações usando Get-ADUser (e alguns outros cmdlets para outras coisas). Estou me conectando a um DC usando o seguinte código PS:

$global:MyDC = ($env:LOGONSERVER -replace "\", "") + ".domain.com"
$ADsession = New-PSSession -computerName $global:MyDC
Invoke-Command -Session $ADsession -Command {Import-Module -Name ActiveDirectory}
Import-PSSession -session $ADsession -module ActiveDirectory -Prefix RM

Algo parecido com isso funciona (então meu PS Remoting parece estar OK):

$strUsersOU = "CN=UserOU,DC=domain,DC=com"
$strResourcesOU = "OU=Resources," + $strUsersOU
$strSharedOU = "OU=Shared," + $strUsersOU

[array]$arrADusers = @(Get-RMADUser -Filter * -SearchBase $strUsersOU -Properties DisplayName, AccountExpirationDate, lockedout, passwordexpired, enabled | Where-Object {($_.DistinguishedName -NotMatch $strSharedOU) -And ($_.DistinguishedName -NotMatch $strResourcesOU)} | sort DisplayName | select DisplayName, lockedout, passwordexpired, enabled, @{Name='AccountExpirationDate';Expression={$_.AccountExpirationDate.ToString('D')}}, DistinguishedName)

Eu me deparei com um problema ao usar Get-RMADUser com uma variável local como parte da expressão de filtro (estou filtrando o atributo de nome distinto do AD). Eu faço algumas coisas com o array acima (carregar em um DGV, etc), então (com base no que o usuário seleciona no DGV) eu recuperar o DN e usá-lo para recuperar informações do usuário do AD com algo parecido com isto (o que parece para funcionar bem quando eu uso um módulo AD PS local):

(Sim, eu consultei todas as propriedades no comando abaixo, mas preciso devolvê-las - mesmo que sejam valores vazios)

$global:objSelectedADuserInfo = Get-ADUser -Filter { DistinguishedName -eq $strSelectedUserDN } -SearchBase $strUsersOU -Properties *, departmentNumber, extensionAttribute1, employeeID, msExchHideFromAddressLists, "msDS-UserPasswordExpiryTimeComputed"

Se eu mudar o comando acima para usar o PS Remoting, obtenho erros com o bloco de script de filtro (mudando Get-ADUser do código acima para Get-RMADUser. Get-RMADUser funciona se eu não usar o filtro. Eu literalmente passei por milhares de artigos de hoje em dia (alguns no StackExchange) sobre como fazer isso funcionar (então eu não vou postar todos os erros diferentes que eu obtive aqui e ao invés eu vou postar código que eu Tenho certeza que estou procurando tanto agora que estou ignorando algo simples. Eu tentei usar coisas como:

(alternando para)

$using:strSelectedUserDN

(e eu tentei algo parecido)

$ScriptBlockFilter = [scriptblock]::Create("DistinguishedName -eq $strSelectedUserDN")

Em seguida, passar a variável $ ScriptBlockFilter para ela como:

Get-RMADUser -Filter $ScriptBlockFilter -SearchBase …

(e depois algo assim:)

Invoke-Command -Session $ADsession -ArgumentList $strSelectedUserDN -ScriptBlock {param ($strSelectedUserDN) Get-RMADUser -Filter { DistinguishedName -eq $strSelectedUserDN } -SearchBase …

(e várias combinações de todos os itens acima). Ainda sem sorte, não posso fazer isso funcionar, não importa o que eu tente fazer. Basicamente, é possível obter um comando como este (abaixo) para trabalhar com uma sessão remota do PS? Usando os comandos acima no início deste thread, estou me conectando ao DC remoto corretamente? O que estou perdendo (ou bagunçando)? Qual é o melhor (adequado) método para fazer algo assim?

$global:objSelectedADuserInfo = Get-RMADUser -Filter { DistinguishedName -eq $strSelectedUserDN } -SearchBase $strUsersOU -Properties *, departmentNumber, extensionAttribute1, employeeID, msExchHideFromAddressLists, "msDS-UserPasswordExpiryTimeComputed"

Agradecemos antecipadamente por qualquer ajuda

    
por STGdb 04.10.2016 / 19:43

2 respostas

2

Eu usei este código para reproduzir sua situação:

$ADPSSession = New-PSSession -ComputerName 2008r2esxi2
Invoke-Command -Session $ADPSSession -Command { Import-Module ActiveDirectory }
Import-PSSession -Session $ADPSSession -Module ActiveDirectory -Prefix RM

Então eu queria tentar um argumento string -Filter, tentei isso e recebi um erro:

$LocalSam = 'joakimbs'
$x = Get-RMADUser -Filter "SamAccountName -eq $LocalSam" -Properties *

Error parsing query: 'SamAccountName -eq joakimbs' Error Message: 'syntax error' at position: '20'.
    + CategoryInfo          : ParserError: (:) [Get-ADUser], ADFilterParsingException

Depois, lembrei-me recentemente de encontrar a sintaxe especial que os cmdlets do AD usam tanto com strings quanto com "blocos de script" (aparentemente, eles não são tratados como blocos de script reais).

Caso seja de interesse (eu acho que é mais limpo que [ScriptBlock] :: Create ()), eu adicionei aspas simples ao redor da variável, e então funciona:

$x = Get-RMADUser -Filter "SamAccountName -eq '$LocalSam'" -Properties *
$x.DistinguishedName
CN=joakimbs,CN=Users,DC=svendsen,DC=local

Espero não estar quebrando nenhuma regra (de novo). Estou respondendo a pergunta, mas principalmente para suplementar informações para futuros leitores. Eu deveria ter feito um comentário?

Eu sinto que devo mencionar que o material [char] 34 é desnecessário. Você poderia apenas dobrar as aspas duplas para ter aspas duplas literais dentro de uma cadeia entre aspas duplas, ou escapar da aspas duplas usando o caractere de escape do PowerShell, um backtick (').

O uso de um bloco de script ainda permite que você pule o char cast (um bloco de script que não é tratado totalmente como eu o entendo, mas ainda aceito como um tipo para o parâmetro -Filter).

Parece ser explicado em este artigo da Microsoft .

Aqui está um exemplo do uso de aspas simples em vez de [char] 34 com [scriptblock] :: Create ().

$SB = [ScriptBlock]::Create("SamAccountName -eq '$LocalSam'")
$SB.ToString()
SamAccountName -eq 'joakimbs'
$x = Get-RMADUser -Filter $SB -Properties *
$x.DistinguishedName
CN=joakimbs,CN=Users,DC=svendsen,DC=local
[char] 34
"

Espero que isso seja útil para alguém.

    
por 16.10.2016 / 00:34
0

Bem, depois de mais tentativas e erros, eu consegui trabalhar com:

$ScriptBlockFilter = [scriptblock]::Create("DistinguishedName -eq " + [char]34 + $strCurrentlySelectedUserDN + [char]34)
$global:objSelectedADuserInfo = (Get-RMADUser -Filter $ScriptBlockFilter -SearchBase $strUsersOU -Properties *, departmentNumber, extensionAttribute1, employeeID, msExchHideFromAddressLists, "msDS-UserPasswordExpiryTimeComputed")

O problema (ao testar originalmente o método do bloco de script) era porque a consulta de filtro Get-RMADuser exigia cotações no lado remoto para filtrar o AD para o nome distinto (e enviar cotações por meio de um script aninhado bloco enquanto a expansão de uma variável não estava funcionando). Então, adicionando [char] 34 em cada lado da variável expandida, coloque as aspas ao redor da variável expandida antes de enviar o comando para a sessão PS remota. Eu também mudei os parênteses ao redor do comando. E uma vez que fiz essas coisas, funcionou (mais ou menos).

Então (em uma nota lateral), me deparei com um problema porque os atributos vazios do AD não estavam sendo retornados da sessão do PS remoto (independentemente de como eu tentei fazer isso funcionar). Quando eu uso Get-ADUser (módulo local do AD), ele retorna propriedades vazias (que eu especifiquei explicitamente no comando Get-ADUser ), mas com uma sessão remota do PS , Get-RMADuser não estava retornando os atributos vazios do AD, como quando utilizava um módulo AD local. Combine isso com a configuração (que está no meu script PS):

Set-StrictMode -Version latest

Isso fazia com que meu script falhasse continuamente sempre que o script tentava acessar uma propriedade que não existia nas propriedades do objeto da sessão PS remota (mesmo que eu tentasse consultá-la explicitamente). O objeto existia quando eu testá-lo com um módulo AD local, mas não com uma sessão PS remota. Assim, ao longo do script, precisei adicionar um teste de validação (como o exemplo abaixo) para garantir que uma propriedade existe antes de tentar acessá-lo no script.

if ($global:objSelectedADuserInfo.PSObject.Properties['employeeID'])
    {
    Blah, blah
    }

Algo que você deve ter em mente ao trabalhar com sessões remotas de PS. De qualquer forma, agora tudo funciona ao usar uma sessão PS remota para consultar o AD para um nome distinto (ou outros filtros) usando uma variável local.

Felicidades !! : -)

    
por 05.10.2016 / 18:45