I am trying to create a new custom object with data input from another object.
$clusters = Get-EMRClusters
$runningclusters = $clusters | Where-Object {
$_.Status.State -eq "Running" -or
$_.Status.State -eq "Waiting"
}
$runningclusters looks like
id name status
-- ---- ------
j-12345 cluster1 running
j-4567 cluster2 running
I want to create a new PSobject $o with a 4th column named PendingShutdown that's a boolean.
id name status pendingshutdown
-- ---- ------ ---------------
j-12345 cluster1 running False
j-4567 cluster2 running False
I have tried running this:
$o = New-Object PSObject
$o | Add-Member -NotePropertyName id -NotePropertyValue $runningclusters.id
$o | Add-Member -NotePropertyName name -NotePropertyValue $runningclusters.name
$o | Add-Member -NotePropertyName status -NotePropertyValue $runningclusters.status.state
$o | Add-Member -NotePropertyName PendingShutdown -NotePropertyValue $true
But my output for $o for the columns id and name are just objects themselves, not rows of IDs. How do I make an object to look like my desired object above?
You need to loop through each of the cluster-objects. You can loop through them and add the column to the current object, like:
$runningclusters = $clusters |
Where-Object {$_.Status.State -eq "Running" -or $_.Status.State -eq "Waiting"} |
Add-Member -NotePropertyName pendingshutdown -NotePropertyValue $true -PassThru
Or you could create new objects per cluster. Ex:
$MyNewClusterObjects = $runningclusters | ForEach-Object {
New-Object -TypeName psobject -Property #{
id = $_.id
name = $_.name
status = $_.status.state
PendingShutdown = $true
}
}
Simply use calculated properties for adding properties to objects in a pipeline, e.g. like this:
$runningclusters = $clusters | Where-Object {
$_.Status.State -eq "Running" -or
$_.Status.State -eq "Waiting"
} | Select-Object *,#{n='PendingShutdown';e={$false}}
Related
I have written powershell scirpt to generate IIS information to CSV.
$bindingip = Get-WmiObject -Namespace root\webadministration -Class sslbinding2 | Select-Object -
Property PSComputerName,IPAddress
$sitedetails = Get-Website | Select-Object -Property Name,ID,State
$report = New-Object psobject
$report | Add-Member -MemberType NoteProperty -Name "Hostname" -Value $bindingip.PSComputerName
$report | Add-Member -MemberType NoteProperty -Name "IPAddress" -Value $bindingip.IPAddress
$report | Add-Member -MemberType NoteProperty -Name "Site Name" -Value $sitedetails.name
$report | Add-Member -MemberType NoteProperty -Name "ID" -Value $sitedetails.id
$report | Add-Member -MemberType NoteProperty -Name "State" -Value $sitedetails.state
$report | Export-Csv C:\content.csv -NoTypeInformation
Output of CSV:
Hostname IPAddress Site Name ID State
System.Object[] System.Object[] System.Object[] System.Object[] System.Object[]
Do i need to add anything to the code to get exact output, Can anyone help on this.
As Abraham and Ash pointed out in their comments, the values of the properties on your variables $bindingip and $sitedetails are an array instead of a string. This is why when you export the report you get the object type instead of it's actual value.
You could see this by doing for example this:
$bindingip.PSComputerName.GetType()
Which would return something like this:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
However if you select just the first element on the array PSComputerName
$bindingip.PSComputerName[0].GetType()
You would see:
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True String System.Object
A workaround for this, is to either convert the values to a multiline string by the use of Out-String or by joining the elements of the arrays with a delimiter i.e. -join '//'.
$bindingip = Get-WmiObject -Namespace root\webadministration -Class sslbinding2
$sitedetails = Get-Website
# Like this (thanks mklement0):
$report = [pscustomobject]#{
Hostname = "$($bindingip.PSComputerName)"
IPAddress = "$($bindingip.IPAddress)"
'Site Name' = "$($sitedetails.name)"
ID = "$($sitedetails.id)"
State = "$($sitedetails.state)"
}
# Or like this:
$report = [pscustomobject]#{
Hostname = ($bindingip.PSComputerName | Out-String).trim()
IPAddress = ($bindingip.IPAddress | Out-String).trim()
'Site Name' = ($sitedetails.name | Out-String).trim()
ID = ($sitedetails.id | Out-String).trim()
State = ($sitedetails.state | Out-String).trim()
}
# Or like this:
$report = [pscustomobject]#{
Hostname = $bindingip.PSComputerName -join '//'
IPAddress = $bindingip.IPAddress -join '//'
'Site Name' = $sitedetails.name -join '//'
ID = $sitedetails.id -join '//'
State = $sitedetails.state -join '//'
}
$report | Export-Csv C:\content.csv -NoTypeInformation
Edit
This can also work, which is what I think mklement0 suggested. It will create a new object for each element on the values of the properties:
$compName = $bindingip.PSComputerName
$ipAddr = $bindingip.IPAddress
$name = $sitedetails.name
$id = $sitedetails.id
$state = $sitedetails.state
$top = ($compName.Count,$ipAddr.Count,$name.Count,$id.Count,$state.Count | Measure-Object -Maximum).Maximum
$report = for($i = 0;$i -lt $top;$i++)
{
[pscustomobject]#{
Hostname = $compName[$i]
IPAddress = $ipAddr[$i]
'Site Name' = $name[$i]
ID = $id[$i]
State = $state[$i]
}
$report | Export-Csv...
In addition to Santiago's very thorough and excellent answer, it appears to me that we are just combining the objects of one array with another to produce all possible combinations. I might do something like the following to accomplish this.
$bindingip = #(
[PSCustomObject]#{
PSComputerName = 'Public1'
IPAddress = '192.168.0.1'
},
[PSCustomObject]#{
PSComputerName = 'Public1'
IPAddress = '127.0.0.1'
}
)
$siteDetails = #(
[PSCustomObject]#{
Name = 'site 1'
Id = 'site1'
State = 'up'
},
[PSCustomObject]#{
Name = 'site 2'
Id = 'site2'
State = 'down'
}
)
$combined = foreach ($ip in $bindingip) {
foreach ($details in $siteDetails) {
$out = [ordered]#{}
$ip.psobject.properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
$details.psobject.properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
[pscustomobject]$out
}
}
$combined | Format-Table
Output
PSComputerName IPAddress Name Id State
-------------- --------- ---- -- -----
Public1 192.168.0.1 site 1 site1 up
Public1 192.168.0.1 site 2 site2 down
Public1 127.0.0.1 site 1 site1 up
Public1 127.0.0.1 site 2 site2 down
One might wrap this in a function for reusability
function Combine-ObjectsFromTwoArrays {
param (
[array]$array1,
[array]$array2
)
foreach ($obj1 in $array1) {
foreach ($obj2 in $array2) {
$out = [ordered]#{}
$obj1.psobject.properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
$obj2.psobject.properties | ForEach-Object {
$out[$_.Name] = $_.Value
}
[pscustomobject]$out
}
}
}
I'm trying to compare two different objects and return the ID of the user from Object 1 if their email address exists in object 2.
I.e. Object 1
| user | id | emailaddress |
+-----------+------------+--------------------+
| test user | asfasfasdf | test.user#test.com |
| ima test | bsvxcffasd | ima.test#test.com |
+-----------+------------+--------------------+
Object 2
+--------------------+
| email |
+--------------------+
| test.user#test.com |
| ima.test#test.com |
+--------------------+
Consider the 2 objects above, my goal objective is to check if a user exists in Object 2 and Object 1. If they exist in Object 2 then I want to return their ID value.
This code is where i'm up to, this will return the users who email address exists in both objects but not their ID:
$x = $object1 | Select-Object -ExpandProperty emailaddress
$y = $object2 | Select-Object -ExpandProperty email
$z = Compare-Object $x $y -IncludeEqual -ExcludeDifferent
$userids = #()
foreach($a in $z.inputobject){
if($object2.email -contains $a){
$userids += $a
}
}
Attempt 2 based on Olaf's reply:
$object1 = New-Object -Typename psobject
$object1 | Add-Member -MemberType NoteProperty -Name email -Value $otherobject.members.email
$object1 | Add-Member -MemberType NoteProperty -Name id -Value $otherobject.members.id
$object2 = New-Object -Typename psobject
$object2 | Add-Member -MemberType NoteProperty -Name email -Value $otherobject2.emailaddress
$ComparedUsers = Compare-Object -ReferenceObject $object1 -DifferenceObject $object2 -IncludeEqual -ExcludeDifferent -PassThru
You should not use -ExpandProperty when you want to use other properties of the object as well. And I'd recommend to use the same property names for both objects.
Something like this should push you to the right direction:
$object1 =
#'
user,id,email
testuser,asfasfasdf,test.user#test.com
imatest,bsvxcffasd,ima.test#test.com
other,lkjshfdlakjs,other.test#test.com
'# |
ConvertFrom-Csv
$object2 =
#'
email
test.user#test.com
ima.test#test.com
any.test#test.com
'# |
ConvertFrom-Csv
Compare-Object -ReferenceObject $object1 -DifferenceObject $object2 -Property 'email' -IncludeEqual -ExcludeDifferent -PassThru
The output of that would be ...
user id email SideIndicator
---- -- ----- -------------
testuser asfasfasdf test.user#test.com ==
imatest bsvxcffasd ima.test#test.com ==
Works:
$Names = 1..5 | % { new-object psobject | add-member -Type NoteProperty -Name Name -Value "MyName" -PassThru } | group Name -AsHashTable
$Names.MyName
Doesn't work:
$Names = 1..5 | % { new-object psobject | add-member -Type ScriptProperty -Name Name -Value {"MyName"} -PassThru } | group Name -AsHashTable
$Names.MyName
The reason you're unable to access the values in the hash-table by prop name or key-based access is that the keys/props are wrapped in PSObjects. There was a Github issue to fix this in Powershell Core, but it will likely remain forever in Windows Powershell.
If you want to convert to a hash-table after grouping, and want to access some of the grouped values by property name or key-based access do this:
$Names = 1..5 | ForEach-Object {
New-Object PsObject | Add-Member -Type ScriptProperty -Name Name -Value { return "MyName"} -PassThru
} | Group-Object -Property 'Name' -AsHashTable -AsString
$Names.MyName
$Names['MyName']
If you want to convert to a hash-table after grouping, and want to access all the grouped values at once, do this:
$Names = 1..5 | ForEach-Object {
New-Object PsObject | Add-Member -Type ScriptProperty -Name Name -Value { return "MyName"} -PassThru
} | Group-Object -Property 'Name' -AsHashTable
$Names.Values
If you're not converting to a hash-table after the grouping, and want to access the data in $Names.Group, you'll need to expand that property.
$Names = 1..5 | % {
new-object psobject | add-member -Type ScriptProperty -Name Name -Value {"MyName"} -PassThru
} | Group-Object -Property 'Name'
$Names | Select-Object -ExpandProperty Group
I have a script that has an array of custom PSObjects with only 2 items each. I need to sort on the first value in each object and then spit out both items, but they are both coming out with #{Name= and #{Line=. Here's a snippet of my code:
$expinfo = #()
<<<>>>
$dbinfo = New-Object -TypeName PSObject
$dbname = Get-Item $folder | select Name
$dbinfo | Add-Member -NotePropertyName DBName -NotePropertyValue "$dbname"
$dbinfo | Add-Member -NotePropertyName ExportLine -NotePropertyValue "$eline"
$expinfo += $dbinfo
<<<>>>
foreach ($expdb in ($expinfo.GetEnumerator() | Sort-Object DBName))
{
$dbn = $expdb | Select-Object -ExpandProperty DBName
$dbe = $expdb | Select-Object -ExpandProperty ExportLine
$dbn
$dbe
}
Output looks like this:
#{Name=AIT1PD}
#{Line=20180312.1700 20180312.1704 All items successfully completed.}
#{Name=APAC1PD}
#{Line=20180313.0100 20180313.0120 All items successfully completed.}
I would like for it to look like this:
AIT1PD 20180312.1700 20180312.1704 All items successfully completed.
APAC1PD 20180313.0100 20180313.0120 All items successfully completed.
Change your Add-Member statements to add the property value you're interested in:
$dbinfo | Add-Member -NotePropertyName DBName -NotePropertyValue $dbname.Name
$dbinfo | Add-Member -NotePropertyName ExportLine -NotePropertyValue $eline.Line
Assuming you're using PowerShell 3.0 or newer, I'd suggest using the [pscustomobject] type accelerator instead:
$dbinfo = [pscustomobject]#{
DBName = (Get-Item $folder).Name
ExportLine = $eline.Line
}
Heres what I have,
Two objects:
$Global:Object1 = New-Object -TypeName PSObject
$Global:Object1 | Add-Member -MemberType NoteProperty -Name Name1 -Value $Name1.Name1
$Global:Object1 | Add-Member -MemberType NoteProperty -Name Name2 -Value $Name2.Name2
$Global:Object2 = New-Object -TypeName PSObject
$Global:Object2 | Add-Member -MemberType NoteProperty -Name Name3 -Value $Name3.Name3
$Global:Object2 | Add-Member -MemberType NoteProperty -Name Name4 -Value $Name4.Name4
Now when I present that to the user, it appears how underneath each other.
I would like to have them presented side by side:
Object1: Object2:
Name1 Name3
Name2 Name4
I will have one more object, but at the moment only 2. Can anyone help me out please?
If the $variables don't make sense, I replaced them to keep things simple..
I do something similar in that I compare two objects side by side for easy reference, I use the following function:
function Compare-ObjectsSideBySide ($lhs, $rhs) {
$lhsMembers = $lhs | Get-Member -MemberType NoteProperty, Property | Select-Object -ExpandProperty Name
$rhsMembers = $rhs | Get-Member -MemberType NoteProperty, Property | Select-Object -ExpandProperty Name
$combinedMembers = ($lhsMembers + $rhsMembers) | Sort-Object -Unique
$combinedMembers | ForEach-Object {
$properties = #{
'Property' = $_;
}
if ($lhsMembers.Contains($_)) {
$properties['Left'] = $lhs | Select-Object -ExpandProperty $_;
}
if ($rhsMembers.Contains($_)) {
$properties['Right'] = $rhs | Select-Object -ExpandProperty $_;
}
New-Object PSObject -Property $properties
}
}
You can test this out with the following, contrived, example:
$object1 = New-Object PSObject -Property #{
'Forename' = 'Richard';
'Surname' = 'Slater';
'Company' = 'Amido';
'SelfEmployed' = $true;
}
$object2 = New-Object PSObject -Property #{
'Forename' = 'Jane';
'Surname' = 'Smith';
'Company' = 'Google';
'MaidenName' = 'Jones'
}
Compare-ObjectsSideBySide $object1 $object2 | Format-Table Property, Left, Right
Which will result in:
Property Left Right
-------- ---- -----
Company Amido Google
Forename Richard Jane
MaidenName Jones
SelfEmployed True
Surname Slater Smith
It wouldn't be difficult to increase the number of objects being compared side-by-side, or even write it in such a way that the function accepts an array of objects which are printed as a side-by-side table.