Passing Active Directory Objects into a Runspace Stripts All Non-Default Properties
See original GitHub issueFor those of you who frequently work with the ActiveDirectory module, you’ll know using Get-ADUser
or Get-ADComputer
will only return a few of the many properties of the object from the database (10 and 9 respectively). If you wish to retrieve additional properties of an AD object, you must specify the -Properties
parameter and provide one or more property names.
When passing an ADObject that contains one or more non-default properties to a runspace as an argument, the property values are not accessible. They can not be accessed with dot-notation, nor used in a Where-Object filter (among other things).
Interestingly enough, if you just pass the object through the runspace script and output it again in the current context, it will now have the missing properties again, as if they were never lost.
I can reproduce the exact same behavior with Start-ThreadJob
, ForEach-Object -Parallel
(which of which uses runspaces) as well as the popular 3rd party module PoshRSJob
. When using either of these method, it makes no difference to which method I use to pass the object into the job. The problem occurs if I use the -ArgumentList
or the $using:
syntax.
This problem however does NOT occur with the standard Start-Job
which spawns an entire new process rather than a new thread/runspace. The downside to using standard background jobs is that the objects are serialized upon passing to the job.
Steps to reproduce
In the example below the Name
property of an ADUser object is “default”, but the City
property must be requested specifically. I output the two properties three times each. Once before, once from within the runspace, and once using the object that was passed through the runspace and back into the current context.
$ADUser = Get-ADUser -identity mander -Properties City
Write-Output "Before Runspace - The value of name is: $($ADUser.Name)"
Write-Output "Before Runspace - The value of city is: $($ADUser.City)"
$Runspace = [runspacefactory]::CreateRunspace()
$PowerShell = [powershell]::Create()
$PowerShell.runspace = $Runspace
$Runspace.Open()
[void]$PowerShell.AddScript({
Param ($ADUser)
$a = "Inside Runspace - The value of name is: $($ADUser.Name)"
$b = "Inside Runspace - The value of city is: $($ADUser.City)"
$c = $ADUser
return @($a,$b,$c)
})
[void]$PowerShell.AddArgument($ADUser)
$a, $b, $c = $PowerShell.Invoke()
$a
$b
Write-Output "After Runspace - The value of name is: $($c.Name)"
Write-Output "After Runspace - The value of city is: $($c.City)"
Expected behavior
Before Runspace - The value of name is: Max Anderson
Before Runspace - The value of city is: Atlanta
Inside Runspace - The value of name is: Max Anderson
Inside Runspace - The value of city is: Atlanta
After Runspace - The value of name is: Max Anderson
After Runspace - The value of city is: Atlanta
Actual behavior
Before Runspace - The value of name is: Max Anderson
Before Runspace - The value of city is: Atlanta
Inside Runspace - The value of name is: Max Anderson
Inside Runspace - The value of city is:
After Runspace - The value of name is: Max Anderson
After Runspace - The value of city is: Atlanta
Environment data
Name Value
---- -----
PSVersion 7.1.0
PSEdition Core
GitCommitId 7.1.0
OS Microsoft Windows 10.0.19041
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Issue Analytics
- State:
- Created 3 years ago
- Comments:15 (2 by maintainers)
Top GitHub Comments
It’s just sorta how it’s stored. The binder looks in
PSObject._instanceMembers
which only contains explicitly added members. You can see the adapted property in_members
if it’s already been retrieved due to caching, but that’s just not where the binder is looking.The AD module uses a custom adapter that just isn’t registered in the new runspace. Issue is fixed if you add
Import-Module ActiveDirectory
in theAddScript
block.