Add values to PS object from other PS command - powershell

I created the following object:
$PSOhash = #{
ConnectedNode = $ConnectedNode
ConnectedNodeDeviceNumber = $ConnectedNodeDeviceNumber
Serialnumber = $Serialnumber
ProductId = $ProductId
}
$ClusterNodeSSDs = New-Object PSObject -Property $PSOhash
and want to add values from the following command into it:
$SSDModel = "xyz123"
$ClusterNode = "Node1"
gwmi -Namespace root\wmi ClusPortDeviceInformation| select ConnectedNode,ConnectedNodeDeviceNumber, Serialnumber, ProductId | sort ConnectedNodeDeviceNumber | where {($_.ConnectedNode -eq $ClusterNode) -and ($_.ProductId -match "$SSDModel")}
which returns the proper informations, but need them as properties in the object for further processing.

If you want to add a set of property-value pairs to an already existing PSObject ($MyObject in this example) that currently does not have those properties, you can use the Add-Member command for this:
$PSOhash = #{
ConnectedNode = $ConnectedNode
ConnectedNodeDeviceNumber = $ConnectedNodeDeviceNumber
Serialnumber = $Serialnumber
ProductId = $ProductId
}
$MyObject = $MyObject | Add-Member -NotePropertyMembers $PSOHash
Explanation:
The -NotePropertyMembers parameter allows you do add a hash table of property-value pairs to a custom object.
Optionally, you can use a combination of Add-Member's -NotePropertyValue and -NotePropertyName to add properties one at a time.
If you want to update one object's property values with property values (same property names) from another object, you can just use direct assignment and the member access operator (.).
$SSDModel = "xyz123"
$ClusterNode = "Node1"
$WmiObjects = Get-WmiObject -Namespace root\wmi ClusPortDeviceInformation |
Select-Object ConnectedNode,ConnectedNodeDeviceNumber, Serialnumber, ProductId |
Sort-Object ConnectedNodeDeviceNumber |
where {($_.ConnectedNode -eq $ClusterNode) -and ($_.ProductId -match "$SSDModel")}
$ClusterNodeSSDs = foreach ($WmiObject in $WmiObjects) {
$PSOhash = #{
ConnectedNode = $WmiObject.ConnectedNode
ConnectedNodeDeviceNumber = $WmiObject.ConnectedNodeDeviceNumber
Serialnumber = $WmiObject.Serialnumber
ProductId = $WmiObject.ProductId
}
[pscustomobject]$PSOhash
}
Explanation:
Note the use of the foreach loop here because the Get-WmiObject will likely return a collection. So you will need to iterate all of them to create custom objects. However, it just seems that you can just use the Get-WmiObject | Select-Object output to perform the same thing.

Related

Format-Table not taking effect (Exchange - powershell)

first of all sorry if my english is not the best. but ill try to explain my issue with as much detail as i can
Im having an issue where i cant get Format-Table to effect the output i give it.
below is the part im having issues with atm.
cls
$TotalSize = $($mailboxes. #{name = ”TotalItemSize (GB)”; expression = { [math]::Round((($_.TotalItemSize.Value.ToString()).Split(“(“)[1].Split(” “)[0].Replace(“,”, ””) / 1GB), 2) } });
$UserN = $($mailboxes.DisplayName)
$itemCount = $($mailboxes.ItemCount)
$LastLogonTime = $($mailboxes.ItemCount)
$allMailboxinfo = #(
#lager dataen som skal inn i et objekt
#{Username= $UserN; ItemCount = $itemCount; LastLogonTime = $($mailboxes.ItemCount); Size = $TotalSize}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$Table = $allMailboxinfo | Format-Table | Out-String
$Table
the output of this gives me what almost looks like json syntax below each title of the table.
Username LastLogonTime ItemCount Size
-------- ------------- --------- ----
{username1, username2,username3,userna...} {$null, $null, $null, $null...} {$null, $null, $null, $null...} {$null, $null, $null, $null...}
running the commands by themselves seem to work tho. like $mailboxes.DisplayName gives the exact data i want for displayname. even in table-format.
the reason im making the table this way instead of just using select-object, is because im going to merge a few tables later. using the logic from the script below.
cls
$someData = #(
#{Name = "Bill"; email = "email#domain.com"; phone = "12345678"; id = "043546" }) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$moreData = #(
#{Name = "Bill"; company = "company 04"}) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
$Merge = #(
#plots the data into a new table
#{Name = $($someData.Name); e_mail = $($someData.email); phone = $($someData.phone); id = $($someData.id); merged = $($moreData.company) }) | % { New-Object object | Add-Member -NotePropertyMembers $_ -PassThru }
#formatting table
$Table = $Merge | Format-Table | Out-String
#print table
$Table
if you are wondering what im doing with this.
My goal, all in all. is a table with using the info from Exchange;
DisplayName, TotalItemSize(GB), ItemCount, LastLogonTime, E-mail adress, archive + Maxquoata, Quoata for mailbox.
You're creating a single object where each property holds an array of property values from the original array of mailbox objects.
Instead, create 1 new object per mailbox:
# construct output objects with Select-Object
$allMailBoxInfo = $mailboxes |Select #{Name='Username';Expression='DisplayName'},ItemCount,#{Name='LastLogonTime';Expression='ItemCount'},#{Name='Size';Expression={[math]::Round((($_.TotalItemSize.Value.ToString()).Split("(")[1].Split(" ")[0].Replace(",", "") / 1GB), 2) }}
# format table
$Table = $allMailBoxInfo | Format-Table | Out-String
# print table
$Table

Get VID PID from Device ID in Powershell

I am fairly new to PowerShell and I want for a USB key plugged, to retrieve some info. Right now my script is:
Get-WmiObject win32_diskdrive |
ForEach-Object{
$disk = $_
$_.GetRelated('Win32_PnPEntity')|
ForEach-Object{
$pnp = $_
$_.GetRelated('Win32_USBController') |
ForEach-Object{
$usb = $_
[pscustomobject]#{
SerialNumber = $disk.SerialNumber
Model = $disk.Model
Size = $disk.Size
if ($usb.DeviceID -match '.*VID_(?<vid>[0-9A-F]{4})&PID_(?<pid>[0-9A-F]{4}).*') {VID=$matches['vid']; PID=$matches['pid']}
}
}
}
}
The line beginning with
if ($usb.DeviceID -match '.*VID_(?<vid>[0-9A-F]{4})&PID_(?<pid>[0-9A-F]{4}).*') {VID=$matches['vid']; PID=$matches['pid']}
does not work. I want to translate deviceid (which I can get by doing USBDeviceID = $usb.DeviceID) ID in PID UID directly.
It throws the following error
Error with code “Missing = operator after key in hash literal" for the statement "if ($usb.DeviceID -match '.* ...
What am I missing ? many thanks for helping me .
Gerald
This is because the way you intend to add properties to the PsCustomObject is wrong.
Either do this:
$result = [PsCustomObject]#{
SerialNumber = $disk.SerialNumber
Model = $disk.Model
Size = $disk.Size
}
# add items to the object if the condition is true
if ($usb.DeviceID -match '.*VID_(?<vid>[0-9A-F]{4})&PID_(?<pid>[0-9A-F]{4}).*') {
$result | Add-Member -MemberType NoteProperty -Name 'VID' -Value $matches['vid']
$result | Add-Member -MemberType NoteProperty -Name 'PID' -Value $matches['pid']
}
# output the PsCustomObject
$result
or use a Hashtable as temporary storage:
# create a Hastable to temporarily store results in
$hash = [ordered]#{
SerialNumber = $disk.SerialNumber
Model = $disk.Model
Size = $disk.Size
}
# add items to the hash if the condition is true
if ($usb.DeviceID -match '.*VID_(?<vid>[0-9A-F]{4})&PID_(?<pid>[0-9A-F]{4}).*') {
$hash['VID']=$matches['vid']
$hash['PID']=$matches['pid']
}
# next cast to PsCustomObject and output
[PsCustomObject]$hash

Creation and modification of Objects in Powershell

I'm a newbie of powershell, I'm starting right now to look at objects, etc.
I'm creating an object in this way:
$myObject = [PSCustomObject]#{
ComputerName = "abc"
Data = "xxx"
}
If I then print $myObject what i get is:
ComputerName Data
------------ ----
abc xxx
And everything is ok, now I want to add a property to that object, and I saw (tell me if i'm wrong) that I can do it in 2 ways: with add-member and with select-object
For example with add-member what I did was:
$myObject | Add-member -NotePropertyName Level -NotePropertyValue Highest
Instead with Select-object I did:
$myobject = 2 (cause i want to add 2 properties, is it right?) | Select-Object -Property Level, Privilege
$myobject.Level = "High"
$myobject.Privilege = "Elevated"
Now if I run $myobject I still only get:
ComputerName Data
------------ ----
abc xxx
What should I do to see all the data, even the one that I added later?
Can I directly add the values to the properties added through Select-Object?
Thanks
You can use the Add-Member method on a PsCustomObject.
$myObject = [PSCustomObject]#{
ComputerName = "abc"
Data = "xxx"
}
$myObject | Add-Member -NotePropertyName Level -NotePropertyValue High
$myObject | Add-Member Privilege Elevated
$myObject
#Output looks like,
ComputerName Data Level Privilege
------------ ---- ----- ---------
abc xxx High Elevated
Update
Not sure at the moment why but will elaborate on it ...
If you print the Pscustomobject and then run the add-member, they seem to be ignored. If you create a hashtable, update it and then convert to PsObject, it works. Following is an example of that hashtable
$myObject = #{
ComputerName = "abc"
Data = "xxx"
}
$myObject | ft
$myObject.Add("Level", "high")
$myObject.Add("Privilege", "Elevated")
[pscustomobject] $myObject | ft
What I found
When you print $myObject then add the data, the data is added but not displayed. This is due to some internal mechanism, unknown to me, that continues to use the same headers from previous command.
If you notice, the data is printed twice under the same heading. If you want to see the differences before and after, pipe it to format-list or format-table to use a different output stream each time.
$myObject = [PSCustomObject]#{
ComputerName = "abc"
Data = "xxx"
}
$myObject | ft
$myObject | Add-Member -NotePropertyName Level -NotePropertyValue High -Force
$myObject | Add-Member Privilege Elevated
$myObject | ft
You can use either Add-Member or Select-Object. Withstanding the advantages or disadvantages in different situations I just want to throw in the Select-Object example. Just so you have both methods:
This example will echo the object with the selected properties:
$myObject |
Select-Object *,
#{Name = 'Level'; Expression = { 'High' } },
#{Name = 'Privilege'; Expression = { 'Elevated' } }
If you want to save the new properties back to the object variable you'll have to reassign like below:
$myObject = $myObject |
Select-Object *,
#{Name = 'Level'; Expression = { 'High' } },
#{Name = 'Privilege'; Expression = { 'Elevated' } }
PowerShell allows you to provide a hash table to define the new properties. You'll note it looks fairly similar to the hash you used to create the object. Typically the expression would leverage the $_ syntax to calculate the property's value. You will often here these referred to as calculated properties.

Find matches in two different Powershell objects based on one property

I am trying to find the matching names in two different types of Powershell objects
$Object1 has two properties - Name (string), ResourceID (uint32)
$object2 has one noteproperty - Name (system.string)
This gives me a list of the matching names but I also want the corresponding resourceID property from $object1.
$computers = Compare-Object $Object1.name $WSD_CM12 | where {$_.sideindicator -eq "=>"} | foreach {$_.inputobject}
These are big objects with over 10,000 items so I'm looking for the most efficient way to accomplish this.
If I'm understanding what you're after, I'd start by creating a hash table from your Object1 collection:
$object1_hash = #{}
Foreach ($object1 in $object1_coll)
{ $object1_hash[$object1.Name] = $object1.ResourceID }
Then you can find the ResourceID for any given Object2.name with:
$object1_hash[$Object2.Name]
Test bed for creating hash table:
$object1_coll = $(
New-Object PSObject -Property #{Name = 'Name1';ResourceID = 001}
New-Object PSObject -Property #{Name = 'Name2';ResourceID = 002}
)
$object1_hash = #{}
Foreach ($object1 in $object1_coll)
{ $object1_hash[$object1.Name] = $object1.ResourceID }
$object1_hash
Name Value
---- -----
Name2 2
Name1 1
Alternative method:
# Create sample list of objects with both Name and Serial
$obj1 = New-Object -Type PSCustomObject -Property:#{ Name = "Foo"; Serial = "1234" }
$obj2 = New-Object -Type PSCustomObject -Property:#{ Name = "Cow"; Serial = "4242" }
$collection1 = #($obj1, $obj2)
# Create subset of items with only Name
$objA = New-Object -Type PSCustomObject -Property:#{ Name = "Foo"; }
$collection2 = #($objA)
#Everything above this line is just to make sample data
# replace $collection1 and $collection2 with $Object1, $WSD_CM12
# Combine into one list
($collection1 + $collection2) |
# Group by name property
Group-Object -Property Name |
# I only want items that exist in both
Where { $_.Count -gt 1 } |
# Now give me the object
Select -Expand Group |
# And get the properties
Where { $_.Serial -ne $null }

Change order of columns in the object

How can I change the column ordering of the output my code produces:
$apps = Import-CSV apps.csv
$computers = Import-CSV compobj.csv
foreach ($computer in $computers) {
$computerLob = $computer.lob
$lobApps = $apps | ? {$_.lob -eq $computerLob }
foreach ($app in $lobApps) {
$computerHostname = $computer.hostname
$appLocation = $app.location
$installed=Test-Path "\\$computerHostname\$appLocation"
New-Object PSObject -Property #{
Computer=$computer.hostname
App=$app.appname
Installed=$installed
}
}
Currently it's producing the columns in the following order: Installed,App,Computer.
I'd like to have it in the following order: Computer,App,Installed.
Powershell V3 added a type accelerator for [PSCustomObject] that creates objects using an ordered hash table, so the properties stay in the order they're declared:
[PSCustomObject] #{
Computer=$computer.hostname
App=$app.appname
Installed=$installed
}
If you want to ensure the output of an object occurs in a certain order i.e. formatted a certain way then use PowerShell's formatting commands.
$obj = [pscustomobject]#{Computer=$computer.hostname;App=$app.appname;Installed=$installed}
$obj | Format-Table Computer,App,Installed
What's more you can better control the output e.g.:
$obj | Format-Table Computer,App,Installed -AutoSize
Or control field width & alignment:
$obj | Format-Table #{label='Computer';expression={$_.Computer};width=20;alignment='right'},App,Installed
Frankly I think it is a better practice to use the formatting commands to get the output order you want rather than to rely upon the order in which the properties are created.
The problem is that you're adding the properties using a hashtable, and hashtables don't preserve order. You can do one of two things:
1. Add the properties in a manner that will set the order (the last line is to output the object to the pipeline):
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name ComputerName -Value $computer.hostname
$obj | Add-Member -MemberType NoteProperty -Name App -Value $app.appname
$obj | Add-Member -MemberType NoteProperty -Name Installed -Value $installed
$obj
2. Determine the order at the time you output the object to the pipeline using Select-Object:
New-Object PSObject -Property #{
Computer=$computer.hostname
App=$app.appname
Installed=$installed
} | select Computer,App,Installed
Which one is preferable depends on how much you'll be working with the object. If, as your question implies, you're only using the PSObject in order to display the information in tabular format, the second method is quicker. If you're going to output the object multiple times in different parts of the script, the first method allows you to simply output it as $obj rather than having to pipe to select every time.
Note also that the second method can be split up like this if you don't want to output the object immediately after populating it:
$obj = New-Object PSObject -Property #{
Computer=$computer.hostname
App=$app.appname
Installed=$installed
}
[...do other stuff...]
$obj | select Computer,App,Installed
Format-Table it's a good solution when you need to display your object fields in a specific order, but it will change the obect and you won't be able to pipe your object, for example when exporting to csv (Export-Csv).
In the case you just want to change "the order of the fields in the object" use Select-Object. This will preserve object type and fields, and still you will be able to pipe the object to other cmdlets.
A more universal way to change the order is using the Select-Object cmdlet with the list of properties in the required order.
See the example:
PS> $ObjectList = 1..3 |
% {New-Object psobject -Property #{P2="Object $_ property P2"; P1="Object $_ property P1"}}
PS> $ObjectList
P2 P1
-- --
Object 1 property P2 Object 1 property P1
Object 2 property P2 Object 2 property P1
Object 3 property P2 Object 3 property P1
PS> $ObjectList | Select-Object P1,P2
P1 P2
-- --
Object 1 property P1 Object 1 property P2
Object 2 property P1 Object 2 property P2
Object 3 property P1 Object 3 property P2
The full form of these commands is the following:
$ObjectList = 1..3 |
ForEach-Object -Process {New-Object -TypeName psobject -Property #{P2="Object $_ property P2"; P1="Object $_ property P1"}}
$ObjectList | Select-Object -Property P1,P2