Printing object properties in Powershell - powershell

When working in the interactive console if I define a new object and assign some property values to it like this:
$obj = New-Object System.String
$obj | Add-Member NoteProperty SomeProperty "Test"
Then when I type the name of my variable into the interactive window Powershell gives me a summary of the object properties and values:
PS C:\demo> $obj
SomeProperty
------------
Test
I basically want to do just this but from within a function in a script. The function creates an object and sets some property values and I want it to print out a summary of the object values to the Powershell window before returning. I tried using Write-Host within the function:
Write-Host $obj
But this just output the type of the object not the summary:
System.Object
How can I have my function output a summary of the object's property values to the Powershell window?

Try this:
Write-Host ($obj | Format-Table | Out-String)
or
Write-Host ($obj | Format-List | Out-String)

My solution to this problem was to use the $() sub-expression block.
Add-Type -Language CSharp #"
public class Thing{
public string Name;
}
"#;
$x = New-Object Thing
$x.Name = "Bill"
Write-Output "My name is $($x.Name)"
Write-Output "This won't work right: $x.Name"
Gives:
My name is Bill
This won't work right: Thing.Name

To print out object's properties and values in Powershell. Below examples work well for me.
$pool = Get-Item "IIS:\AppPools.NET v4.5"
$pool | Get-Member
TypeName: Microsoft.IIs.PowerShell.Framework.ConfigurationElement#system.applicationHost/applicationPools#add
Name MemberType Definition
---- ---------- ----------
Recycle CodeMethod void Recycle()
Start CodeMethod void Start()
Stop CodeMethod void Stop()
applicationPoolSid CodeProperty Microsoft.IIs.PowerShell.Framework.CodeProperty
state CodeProperty Microsoft.IIs.PowerShell.Framework.CodeProperty
ClearLocalData Method void ClearLocalData()
Copy Method void Copy(Microsoft.IIs.PowerShell.Framework.ConfigurationElement ...
Delete Method void Delete()
...
$pool | Select-Object -Property * # You can omit -Property
name : .NET v4.5
queueLength : 1000
autoStart : True
enable32BitAppOnWin64 : False
managedRuntimeVersion : v4.0
managedRuntimeLoader : webengine4.dll
enableConfigurationOverride : True
managedPipelineMode : Integrated
CLRConfigFile :
passAnonymousToken : True
startMode : OnDemand
state : Started
applicationPoolSid : S-1-5-82-271721585-897601226-2024613209-625570482-296978595
processModel : Microsoft.IIs.PowerShell.Framework.ConfigurationElement
...

Tip #1
Never use Write-Host.
Tip #12
The correct way to output information from a PowerShell cmdlet or function is to create an object that contains your data, and then to write that object to the pipeline by using Write-Output.
-Don Jones: PowerShell Master
Ideally your script would create your objects ($obj = New-Object -TypeName psobject -Property #{'SomeProperty'='Test'}) then just do a Write-Output $objects. You would pipe the output to Format-Table.
PS C:\> Run-MyScript.ps1 | Format-Table
They should really call PowerShell PowerObjectandPipingShell.

Some general notes.
$obj | Select-Object ⊆ $obj | Select-Object -Property *
The latter will show all non-intrinsic, non-compiler-generated properties. The former does not appear to (always) show all Property types (in my tests, it does appear to show the CodeProperty MemberType consistently though -- no guarantees here).
Some switches to be aware of for Get-Member
Get-Member does not get static members by default. You also cannot (directly) get them along with the non-static members. That is, using the switch causes only static members to be returned:
PS Y:\Power> $obj | Get-Member -Static
TypeName: System.IsFire.TurnUpProtocol
Name MemberType Definition
---- ---------- ----------
Equals Method static bool Equals(System.Object objA, System.Object objB)
...
Use the -Force.
The Get-Member command uses the Force parameter to add the intrinsic members and compiler-generated members of the objects to the display. Get-Member gets these members, but it hides them by default.
PS Y:\Power> $obj | Get-Member -Static
TypeName: System.IsFire.TurnUpProtocol
Name MemberType Definition
---- ---------- ----------
...
pstypenames CodeProperty System.Collections.ObjectModel.Collection...
psadapted MemberSet psadapted {AccessRightType, AccessRuleType,...
...
Use ConvertTo-Json for depth and readable "serialization"
I do not necessary recommend saving objects using JSON (use Export-Clixml instead).
However, you can get a more or less readable output from ConvertTo-Json, which also allows you to specify depth.
Note that not specifying Depth implies -Depth 2
PS Y:\Power> ConvertTo-Json $obj -Depth 1
{
"AllowSystemOverload": true,
"AllowLifeToGetInTheWay": false,
"CantAnyMore": true,
"LastResortOnly": true,
...
And if you aren't planning to read it you can -Compress it (i.e. strip whitespace)
PS Y:\Power> ConvertTo-Json $obj -Depth 420 -Compress
Use -InputObject if you can (and are willing)
99.9% of the time when using PowerShell: either the performance won't matter, or you don't care about the performance. However, it should be noted that avoiding the pipe when you don't need it can save some overhead and add some speed (piping, in general, is not super-efficient).
That is, if you all you have is a single $obj handy for printing (and aren't too lazy like me sometimes to type out -InputObject):
# select is aliased (hardcoded) to Select-Object
PS Y:\Power> select -Property * -InputObject $obj
# gm is aliased (hardcoded) to Get-Member
PS Y:\Power> gm -Force -InputObject $obj
Caveat for Get-Member -InputObject:
If $obj is a collection (e.g. System.Object[]), You end up getting information about the collection object itself:
PS Y:\Power> gm -InputObject $obj,$obj2
TypeName: System.Object[]
Name MemberType Definition
---- ---------- ----------
Count AliasProperty Count = Length
...
If you want to Get-Member for each TypeName in the collection (N.B. for each TypeName, not for each object--a collection of N objects with all the same TypeName will only print 1 table for that TypeName, not N tables for each object)......just stick with piping it in directly.

The below worked really good for me. I patched together all the above answers plus read about displaying object properties in the following link and came up with the below
short read about printing objects
add the following text to a file named print_object.ps1:
$date = New-Object System.DateTime
Write-Output $date | Get-Member
Write-Output $date | Select-Object -Property *
open powershell command prompt, go to the directory where that file exists and type the following:
powershell -ExecutionPolicy ByPass -File is_port_in_use.ps1 -Elevated
Just substitute 'System.DateTime' with whatever object you wanted to print. If the object is null, nothing will print out.

# Json to object
$obj = $obj | ConvertFrom-Json
Write-host $obj.PropertyName

Related

How can I change a System.Array into a System.Object?

I have a variable stored as a System.Array displayed as;
Site : https://value.sharepoint.com/
Email : value#value.co.uk
DisplayName : value
UniqueId : value
AcceptedAs : value#value.co.uk
WhenCreated : 24/01/2019 06:02:45
InvitedBy : value_value.co.uk#ext##value.onmicrosoft.com
When I try to export this variable as a file it shows in the same format. As this is not in the correct structure for a table (shown below) I am unable to use this data when I try to use it in Power BI.
Site Email ect
---- ---- ----
https://value.sharepoint.com/ value#value.co.uk ect
I need to get my data into the structure shown above. I have tried;
$Test = New-Object -TypeName PSObject -Property $ExternalUsers
However this results in the following error;
New-Object : Cannot convert 'System.Object[]' to the type 'System.Collections.IDictionary' required by parameter 'Property'. Specified method is not supported.
I then tried to loop through all of the items in the array and then create an object for each item, before adding it to a "Master Object";
foreach($var in $ExternalUsers){
$Test = New-Object -TypeName PSObject -Property $ExternalUsers
$Test | Add-Member -MemberType NoteProperty -Name Site -Value $var.Site
$Test | Add-Member -MemberType NoteProperty -Name Email -Value $var.Email
$TestObject += $Test
}
This got each item into the correct structure but when I tried to add all the items back into the one variable I got the error;
Method invocation failed because [System.Management.Automation.PSObject] does not contain a method named 'op_Addition'.
Any ideas how I could get around this?
To me it looks like you have an array (System.Object[]) containing PSObjects with the properties Site, Email etc.
A structure like that is ideal for exporting to CSV file, which you can then import in a spreadsheed application like Excel for instance.
For that you use the cmdlet Export-Csv like this:
$ExternalUsers | Export-Csv -Path 'PATH AND FILENAME FOR THE OUTPUT CSV FILE' -NoTypeInformation
If the output you show is complete, it seems there is only one element in the array. You can check this by looking at $ExternalUsers.Count.
I'm not quite sure what you mean by "As this is not in the correct structure for a table", because you can quite easily display it as table using
$ExternalUsers | Format-Table -AutoSize
Output on console window:
Site Email DisplayName UniqueId AcceptedAs WhenCreated InvitedBy
---- ----- ----------- -------- ---------- ----------- ---------
https://value.sharepoint.com/ value#value.co.uk value value value#value.co.uk 24/01/2019 06:02:45 value_value.co.uk#ext##value.onmicrosoft.com
If what you want is less properties, just loop through the array and select the properties you want to keep from the objects in it:
$Shortened = $ExternalUsers | ForEach-Object {
$_ | Select-Object Site, Email
}
$Shortened | Format-Table -AutoSize
Will produce:
Site Email
---- -----
https://value.sharepoint.com/ value#value.co.uk
I'm not into Power BI, but remember that the Format-Table cmdlet is for display purposes on console ONLY.
It does NOT provide anything else but a view on the data.
Hope this helps
use:
$TestObject = #()
and no need to specify (-Property $ExternalUsers)
$Test = New-Object -TypeName PSObject

powershell outputs argument with (#{Name=name}:String)

I'm trying to run the command Get-VMNetworkAdapter on a list of VMs
I'm getting the list with the command:
Get-VM –ComputerName (Get-ClusterNode –Cluster clustername)|select name
and it looks fine, when I'm using
$vmm=Get-VM –ComputerName (Get-ClusterNode –Cluster clustername)|select name
foreach ($item in $vmm)
{Get-VMNetworkAdapter -VMName $item}
it gives me the exception
nvalidArgument: (#{Name=vmname}:String)
like it adds all those symbols..
What is the proper way to lose them?
You need to expand the property. Select doesn't remove the object otherwise:
$vmm = Get-VM –ComputerName (Get-ClusterNode –Cluster clustername) `
| Select-Object -ExpandProperty name
To explain what -ExpandProperty does:
First of all, the drawback of -ExpandProperty is that you can only do it to one property at a time.
Select-Object normally wraps the results in another object so that properties remain properties. If you say $x = Get-ChildItem C:\Windows | Select-Object Name, then you get an object array with one property: Name.
PS C:\> $x = Get-ChildItem C:\Windows | Select-Object Name
PS C:\> $x
Name
----
45235788142C44BE8A4DDDE9A84492E5.TMP
8A809006C25A4A3A9DAB94659BCDB107.TMP
.
.
.
PS C:\> $x[0].Name
45235788142C44BE8A4DDDE9A84492E5.TMP
PS C:\> $x[0].GetType().FullName
System.Management.Automation.PSCustomObject
Notice the header? Name is a property of the object.
Also, the base object with it's type is still kind of there:
PS C:\> $x | Get-Member
TypeName: Selected.System.IO.DirectoryInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Name NoteProperty string Name=45235788142C44BE8A4DDDE9A84492E5.TMP
TypeName: Selected.System.IO.FileInfo
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
Name NoteProperty string Name=bfsvc.exe
Normally, that's all great. Especially because we normally want multiple properties of the object.
Sometimes, however, not what we want. Sometimes, we want an array that's the same type as the property we selected. When we use it later we want just that property and nothing else and we want it to be the exact same type as the property and nothing else.
PS C:\> $y = Get-ChildItem C:\Windows | Select-Object -ExpandProperty Name
PS C:\> $y
45235788142C44BE8A4DDDE9A84492E5.TMP
8A809006C25A4A3A9DAB94659BCDB107.TMP
.
.
.
PS C:\> $y[0].Name
PS C:\> $y[0]
45235788142C44BE8A4DDDE9A84492E5.TMP
PS C:\> $y.GetType().FullName
System.Object[]
PS C:\> $y[0].GetType().FullName
System.String
Notice there's no header, and any calls to a Name property fail; there is no Name property anymore.
And, there's nothing left over from the original object:
PS C:\> $y | Get-Member
TypeName: System.String
Name MemberType Definition
---- ---------- ----------
Clone Method System.Object Clone(), System.Object ICloneable.Clone()
.
.
.
.
Basically, here it's the equivalent of doing this:
$z = Get-ChildItem C:\Windows | ForEach-Object { $_.Name }
Which I think is how you had to do it in PowerShell v1.0 or v2.0... it's been too many years since I've used that to remember right.

Using a Powershell noteproperty as a text string in a variable

I've used Invoke-Restmethod to download some data, which Powershell stores in a PSCustomObject, in a property called data.
I need to use the value of one of the items in the returned data as a variable for another command. I have managed to select-object -expand my way down to the following output from Get-Member:
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
id NoteProperty System.Int32 id=999
What I need to do is grab the value of the ID noteproperty - 999 - and pass that as part of a string to a new variable, eg:
$newVar = "sometext" + 999 + "moretext"
No amount of select-string or out-string etc is helping. Scripting is not exactly my strong point so I'm not sure I'm even articulating what I want properly - apologies if this is the case!
Any assistance much appreciated
I'm not sure exactly what your code and looks like, so I created the following static approximation from the description:
$data = New-Object PSCustomObject
$data | Add-Member -Type NoteProperty -Name Id -Value 999
$restResponse = New-Object PSCustomObject
$restResponse | Add-Member -Type NoteProperty -Name data -Value $data
Please clarify if this is not a match. You can get the Id value as follows
$restResponse.data.Id
Assign it to another variable
$newVar = "sometext" + $restResponse.data.Id + "moretext"
$newVar
And if your REST response is a collection of data objects, iterate through them
$restResponse.data | Foreach-Object { "sometext" + $_.Id + "moretext" }
I would go for for using $output | select *,#{n='test';e={[string]$_.test}} -exclude properties test
if the exclude is not active it will complain about it already exists. Mostly I use the select expression to manipulate data realtime instead of psCustomObject for such simple task

How to refer to entire pipeline object without a script block

With the advent of PowerShell V3 instead of having to write:
Get-Process | Where { $_.ProcessName -match "win" }
...one could now write the more terse:
Get-Process | Where ProcessName -match "win"
... a clear win (ahem) for shell use.
Now let's say I had a simple array of strings, call it $stuff. Is it possible to reduce this:
$stuff | Where { $_ -match "win" }
...in an analogous way to the first example, i.e. removing the script block and referring to the entire object, in this case?
To reduce
$stuff | Where { $_ -match "win" }
you can always do it like this (works for all powershell version):
$stuff = "a","b","win", "winona", "d", "windows"
$stuff -match "win"
win
winona
windows
The form Get-Process | Where ProcessName -match "win" is called comparison statement. It's poorly a documented feature, as Where-Object's documentation doesn't really explain what those are about.
The reason comparison statement works for Get-Process but not for $stuff is that the former returns an array of System.Diagnostics.Process objects whlist the later is String. The comparison statements expects property name for filtering.
Let's look at what's available in each array member. First off processes like so,
$proc = get-process
gm -InputObject $proc[0] -MemberType property
TypeName: System.Diagnostics.Process
Name MemberType Definition
...
ProcessName Property string ProcessName {get;}
...
So, there is a ProcessName property, thus Where-Object can filter with it as seen.
The string array:
$stuff = #("foo", "bar", "zoffo", "qazzer")
gm -InputObject $stuff[0] -MemberType property
TypeName: System.String
Name MemberType Definition
Length Property int Length {get;}
There is only one property in a String which is it's length. Sure enough, it can be used for filtering like so,
$stuff | where length -ne 3
zoffo
qazzer
$stuff | where length -eq 3
foo
bar
As there are no other property typed members, the only way to filter the string array is by the classic script block mode.

Powershell - When an object is placed on the pipeline, can it be used again later?

I have a collection of objects which I am trying to return multiple sets of data from. Each time I want to select one of the properties of the object and sort it uniquely.
The problem I'm getting is that I only get results the first time I use the collection. This leads me to wonder whether the objects are being "consumed" when I put them on the pipeline.
To illustrate with an example:
$results = getMyCollectionOfObjects
$results | select-object property1 | sort-object -unique property1
$results | select-object property2 | sort-object -unique property2
$results | select-object property3 | sort-object -unique property3
As far as I can tell this should result in 3 distinct lists of all the possible values for each of the three properties.
However for me it's like the $results value is being "used up" the first time it is selected from.
Is this the case and what should I do instead?
Hope this is clear.
That's how it rolls... you have to explicitly pipe them to Out-Default to get rid of that odd behavior. Otherwise it will try to display property1 for 2nd and 3rd set too. You removed it from $results, so it comes back blank.
HTH
Bartek
So this had me tripped up and scratching my head or a minute as well. The answer it turns out is really quite simple: the Select-Object cmdlet returns an object of type Selected.System.Management.Automation.PSCustomObject. That is then passed down the pipeline to following two selects, but since there are no longer any matching properties (they were discarded from the first select) - nothing is output. Consider the following example:
# declare an array and populate it
$results = #()
$results = $results + (New-Object PSobject -Property #{
P1 = "One"
P2 = "Two"
P3 = "Three"
})
$results = $results + (New-Object PSobject -Property #{
P1 = "Uno"
P2 = "Dos"
P3 = "Tres"
})
$results | select P1
$results | select P2
$results | select P3
As you described, I was only getting output from the first select. I them took the suggestion from BartekB to put | Out-Default at the end of each line and it started working. I investigated further by replacing that with | Get-Member to view the object the was being placed on the pipeline:
$results | select -Property P1 | get-member
TypeName: Selected.System.Management.Automation.PSCustomObject
Name MemberType Definition
---- ---------- ----------
Equals Method bool Equals(System.Object obj)
GetHashCode Method int GetHashCode()
GetType Method type GetType()
ToString Method string ToString()
P1 NoteProperty System.String P1=One
In a nutshell, the Out-Default is required to actually force it to display on the console instead of passing it along to the next statement. It would seem this behavior is implied upon completing a statement in the interactive shell, but behaves a bit differently when fully scripted.