Powershell – PS Remoting to AD, Get-ADUser, and script blocks with local variables

active-directorypowershell

I am trying to write a PS script to manage AD objects by connecting to a DC and retrieving information using Get-ADUser (and some other cmdlets for other things). I’m connecting to a DC using the following PS code:

$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

Something like this works (so my PS Remoting seems to be 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)

I’ve run into an issue when using Get-RMADUser with a local variable as part of the filter expression (I’m filtering on the AD distinguished name attribute). I do some stuff with the above array (load into a DGV, etc.) then (based on what the user selects in the DGV) I retrieve the DN and use it to then retrieve user info from AD with something like this (which seems to work fine when I use a local AD PS module):

(Yes, I query all properties in the below command abut I need to return them – even if they are empty values)

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

If I switch the above command to use PS Remoting, I get errors with the filter script block (changing Get-ADUser from above code to Get-RMADUser. Get-RMADUser works if I don't use the filter. I’ve literally gone through a thousand KB articles today (some on StackExchange) about how to make this work (so I won’t post all of the different errors that I’ve gotten here and instead I’ll just post code that I’ve tried). I’m sure I’m searching so hard by now that I’m overlooking something simple. I’ve tried using things like:

(switching to)

$using:strSelectedUserDN

(and I've tried something like)

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

Then passing the $ScriptBlockFilter variable to it like:

Get-RMADUser -Filter $ScriptBlockFilter -SearchBase …

(and then something like this:)

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

(and various combinations of all of the above). Still no luck, I can’t get this to work no matter what I try to do. Basically, is it possible to get a command like this one (below) to work with a remote PS session? Using the above commands at the start of this thread, am I connecting to the remote DC properly? What am I missing (or messing up on)? What is the best (proper) method for doing something like this?

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

Thanks much in advance for any help

Best Answer

I used this code to reproduce your situation:

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

Then I wanted to try a string -Filter argument, tried this, and got an error:

$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

Then I remembered recently encountering the special syntax the AD cmdlets use both with strings and "script blocks" (apparently they aren't trated as real script blocks).

In case it's of interest (I think it's cleaner than [ScriptBlock]::Create()), I added single quotes around the variable, and it then works:

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

I hope I'm not breaking any rules (again). I am answering the question, but mostly for the sake of supplementing information for future readers. Should I have made this a comment?

I feel that I should also mention that the [char] 34 stuff is unnecessary. You could just double up the double quotes to have literal double quotes inside a double-quoted string, or escape the double quote using the PowerShell escape character, a backtick (`).

Using a script block still allows you to skip the char cast (a script block that's not treated fully like one as I understand it, but still accepted as a type for the -Filter parameter).

It seems to be explained in this Microsoft article.

Here's an example of using single quotes instead of [char] 34 with [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
"

Hope that's helpful to someone.