Add new property based on a different objects property - arrays

I'm trying to search through one column in each row of the table. I would then like to add another value to the row based on the number being search.
This code produces the table:
$LUNSSummary = ($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' | foreach {
$Stringdata = $_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$LUNSSummary |
select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups' |
Format-Table -AutoSize
Then I have this code which can search using the "Logical Unit Number" and produce the desired output. In this example the -contains is 1029 from the above screenshot.
$data = $LUNS_in_Pools | Out-String
$pools = $data -replace ': +','=' -split "`r`n`r`n" |
% { New-Object -Type PSCustomObject -Property (ConvertFrom-StringData $_) } |
select -Property *,#{n='LUNs';e={$_.LUNs -split ', '}} -Exclude LUNs
$pools | ? { $_.LUNs -contains 1029 } | select -Expand 'Pool Name'
Which produces in this case "Pool 2". The result can be Pool 1-99.
I want to combine these two codes to search every "Logical Unit Number" and add the result to the end of the table in a 5th section/column "Pools".
EDIT
As requested, raw data:
$NY_LUNS before $LUNSSummary gets it: http://pastebin.com/5wrd51Lf
$LUNS_in_Pools raw data: http://pastebin.com/Zg9q6jhe
Desired Output: (Pool is obtained from "Logical Unit Number")
EDIT 2
This is now the closest to correct so far, it prints the same pool result every time.
$LUNSSummary =
($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' |
foreach { $Stringdata =
$_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$data = $LUNS_in_Pools | Out-String
$pools = $data -replace ': +','=' -split "`r`n`r`n" |
% { New-Object -Type PSCustomObject -Property (ConvertFrom-StringData $_) } |
select -Property *,#{n='LUNs';e={$_.LUNs -split ', '}} -Exclude LUNs
$poolProperty = #{Label="Pool";Expression={$pools | ? { $_.LUNs -contains [int]$_.'LOGICAL UNIT NUMBER'} | select -Expand 'Pool Name'}}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
if I check the output of $pools | ? { $_.LUNs -contains [int]$_.'LOGICAL UNIT NUMBER'} | select -Expand 'Pool Name'
I only see one result. I'm thinking maybe it has to be looped some how?

From the guess of it you just need one more calculated property on the end there for 'Pool'. You already have, and tested, the logic. Just need to implement it.
$poolProperty = #{Label="Pool";Expression={
$lunID = $_.'LOGICAL UNIT NUMBER';
$pools | Where-Object{$_.LUNs -contains $lunID} |
Select-Object -Expand 'Pool Name'}
}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
We take the LOGICAL UNIT NUMBER of the current item in the pipeline and save it so that we can start another to extract the match from the $pools object. As long as you luns are exclusive this would always return one Pool Name.
The above should work but I changed how $pools was created so it matched the logic of $LUNSSummary. I used here-strings for the raw data from your paste bin.
$LUNSSummary = ($NY_LUNS) -split '\s+(?=LOGICAL UNIT NUMBER)' |
foreach { $Stringdata =
$_.replace(':','=')
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
}
$pools = ($LUNS_in_Pools | Out-String) -split '\s+(?=Pool Name)' | ForEach-Object{
New-Object -Type PSCustomObject -Property (ConvertFrom-StringData ($_ -replace ":","=")) |
Select -Property *,#{n='LUNs';e={$_.LUNs -split ',\s*'}} -Exclude LUNs
}
$poolProperty = #{Label="Pool";Expression={
$lunID = $_.'LOGICAL UNIT NUMBER';
$pools | Where-Object{$_.LUNs -contains $lunID} |
Select-Object -Expand 'Pool Name'}
}
$LUNSSummary | select 'Name','LOGICAL UNIT NUMBER','State','LUN Capacity(Megabytes)','LU Storage Groups',$poolProperty
Looks like $LUNS_in_Pools was a newline delimited string. Piping to Out-String cleaned it up to remove the newlines and allow the regex/ConvertFrom-StringData to work.

Related

How to Out-Gridview multiple values to one key in Powershell?

I have a hashtable of IP connections that are associated to their Destination Prefixes. Here is the code to gather it all together:
function Get-InterfaceRoutes {
$interfaceIPs = Get-NetIPConfiguration -Detailed |
Where-Object { $_.netadapter.status -eq 'up' } | Get-NetIPAddress -AddressFamily IPv4 |
Select-Object -Property IPAddress, InterfaceIndex, InterfaceAlias
Foreach ($interfaceIP in $interfaceIPs) {
$route = Get-NetRoute -InterfaceIndex ($interfaceIP.InterfaceIndex) |
Select-Object -Property ifINdex, DestinationPrefix, NextHop, RouteMetric, ifMetric |
Where-Object -Property DestinationPrefix -like '*.*.*.*' | Sort-Object -Property ifIndex
[PSCustomObject]#{
Index = ($interfaceIp.InterfaceIndex)
Address = ($interfaceIP.IPAddress)
Alias = ($interfaceIP.InterfaceAlias)
DestinationPrefix = ($route.DestinationPrefix)
NextHop = ($route.NextHop)
RouteMetric = ($route.RouteMetric)
InterfaceMetric = ($route.InterfaceMetric)
}
}
}
$collection = #(Get-InterfaceRoutes)
I am building a UI in PS-5.1(WinForms) to list the various indexes and their properties. With it I have this button that I want to be able to select one of the listed Destination Prefixes (of which there will be at least 1, at most n to choose from) associated with each index (again, 1-n):
$destinationSelectButton.Add_Click({
$selectedDestination = $collection.keys |
Out-GridView -Title "Select Destination Prefix" -PassThru |
ForEach-Object { $_.Values } | Select-Object -Property DestinationPrefix
Write-Host $selectedDestination | Out-String #<<<exists for confirmation in console, ignore.
})
The problem I have with this snippet specifically is that when I select the button, I don't get the GridView box to select from the list of Prefixes. Just nothing. No error message, no window opening, just an acknowledgement in my terminal that the button was clicked.
If I arrange the code any other way, such as:
$selectedDestination = $collection |
Out-Gridview -Title "Select Destination Prefix" -PassThru |
Select-Object -Property DestinationPrefix
I get this:
Here the Destination Prefix is gathered as one object, but I want to display that array broken apart so that one can be selected from the list and sent to $selectedDestination for use later on. I suspect the code block I shared for the button SHOULD do just that, but without the window opening, and no error to say why, I am not sure where to go from here to get it to work.
If I understand correctly, you're just needing to loop through each object resulted from Get-NetRoute and then combine / merge that output with the result of Get-NetIPConfiguration, instead of merging all objects into one object.
For that you can use Select-Object with calculated properties:
$interfaceIPs = Get-NetIPConfiguration -Detailed |
Where-Object { $_.NetAdapter.Status -eq 'up' } |
Get-NetIPAddress -AddressFamily IPv4
$collection = foreach($interfaceIP in $interfaceIPs) {
Get-NetRoute -InterfaceIndex $interfaceIP.InterfaceIndex |
Where-Object -Property DestinationPrefix -like '*.*.*.*' |
Sort-Object -Property ifIndex | Select-Object #(
#{ N = 'Index'; E = { $interfaceIp.InterfaceIndex }}
#{ N = 'Address'; E = { $interfaceIP.IPAddress }}
#{ N = 'Alias'; E = { $interfaceIP.InterfaceAlias }}
'DestinationPrefix'
'NextHop'
'RouteMetric'
'InterfaceMetric'
)
}
$selection = $collection | Out-GridView -PassThru

Can I combine these two arrays

I have some simple Exchange Powershell I have written. I would like to list the UPN, Displayname, Item Count, and Item Size into a single CSV. However I have only been able to successfully push the data to two arrays and then manually combine them. Here is my code.
$MailBoxs = Get-Mailbox * | Select UserPrincipalName -ExpandProperty UserPrincipalName | Sort-Object UserPrincipalName
$Mailboxs2 = $MailBoxs.Where({ $_ -ne $null })
ForEach($MailBox2 in $MailBoxs2) { Get-MailboxStatistics $Mailbox2 | Sort-Object TotalItemSize –Descending | Select #{label=”User”;expression={$_.DisplayName}},#{label=”Total Size (MB)”;expression={$_.TotalItemSize.Value.ToMB()}},#{label=”Items”;expression={$_.ItemCount}} | Export-CSV "C:\T2\MailBoxSize.csv" -Append -NoTypeInformation }
ForEach($MailBox2 in $MailBoxs2) { $Mailbox2 | Export-CSV "C:\T2\MailBoxSize2.csv" -Append -NoTypeInformation }
Basically the second CSV gives me two fields for some reason the SMTP address and some random Length field, It also gives me a leading whitespace. If anyone has any ideas on how to clean this up I would love to hear them. Thanks for your time.
The multiple select statements were unnecessary. Here's a bit simplified way:
$mailboxes = #(Get-Mailbox *).
Where({$_.UserPrincipalName}) |
Sort-Object -Property UserPrincipalName
foreach ($box in $mailboxes) {
Get-MailboxStatistics $box.UserPrincipalName |
Sort-Object -Property TotalItemSize -Descending |
Select-Object -Property #(
#{L='UPN';E={$box.UserPrincipalName}}
#{L='User';E={$_.DisplayName}}
#{L='Total Size (MB)';E={$_.TotalItemSize.Value.ToMB()}}
#{L='Items';E={$_.ItemCount}}
) |
Export-Csv -Path 'C:\T2\MailBoxSize.csv' -NoTypeInformation -Append
}

Display all values in PSCustomObject array [duplicate]

Is it possible to display the results of a PowerShell Compare-Object in two columns showing the differences of reference vs difference objects?
For example using my current cmdline:
Compare-Object $Base $Test
Gives:
InputObject SideIndicator
987654 =>
555555 <=
123456 <=
In reality the list is rather long. For easier data reading is it possible to format the data like so:
Base Test
555555 987654
123456
So each column shows which elements exist in that object vs the other.
For bonus points it would be fantastic to have a count in the column header like so:
Base(2) Test(1)
555555 987654
123456
Possible? Sure. Feasible? Not so much. PowerShell wasn't really built for creating this kind of tabular output. What you can do is collect the differences in a hashtable as nested arrays by input file:
$ht = #{}
Compare-Object $Base $Test | ForEach-Object {
$value = $_.InputObject
switch ($_.SideIndicator) {
'=>' { $ht['Test'] += #($value) }
'<=' { $ht['Base'] += #($value) }
}
}
then transpose the hashtable:
$cnt = $ht.Values |
ForEach-Object { $_.Count } |
Sort-Object |
Select-Object -Last 1
$keys = $ht.Keys | Sort-Object
0..($cnt-1) | ForEach-Object {
$props = [ordered]#{}
foreach ($key in $keys) {
$props[$key] = $ht[$key][$_]
}
New-Object -Type PSObject -Property $props
} | Format-Table -AutoSize
To include the item count in the header name change $props[$key] to $props["$key($($ht[$key].Count))"].

Call a Group Object by Name in ForEach Loop

I've imported a CSV file into an array and grouped it by email address. I'd like to be able to call the group by name in another ForEach loop so that I can send an email to each email address with a table of the $ServerID(s) and $DateTime(s) associated with them. Was Group-Object the best way to start this?
In the CSV file, one email address can be associated with many servers, but one server is only associated with one email address. The values in the file will be updated constantly - thus the need for dynamic variables for almost all objects.
$csv = import-csv "C:Example.csv"
$array = #()
ForEach ($server in $csv) {
$object = New-Object PSObject -Property #{"Email"= $server.Email; "ServerID" = $server.ServerID; "DateTime" = $server.DateTime}
$array += $object}
$array | Group-Object Email
Count Name Group
2 A#gmail.com {#{PG_Email=A#gmail.com; ServerID=333; Lease_End=10/10/15}, #{PG_Email=A#gmail.com; ServerID=111; Lease_End=12/12/15}}
1 B#gmail.com {#{PG_Email=B#gmail.com; ServerID=222; Lease_End=09/09/15}}
Yes, you should use Group-Object. I'm not sure what you mean with "call the group by name". If you want to select a group based by the name you would have to save the result from Group-Object and search for your group like:
$groups = $array | Group-Object Email
$selectedmail = "B#gmail.com"
$groups | Where-Object { $_.Name -eq $selectedmail }
Or you could save the groups in a hashtable:
#Create hashtable
$ht = #{}
#Fill hashtable with groups (email as key/ID)
$array | Group-Object Email | ForEach-Object { $ht[$_.Name] = $_.Group }
$selectedmail = "B#gmail.com"
#Get objects for selected mail
$ht[$selectedmail]
If you are going to mail everyone, I would simplify this to:
Import-Csv "C:\Example.csv" |
Group-Object Email |
ForEach-Object {
$to = $_.Name
#Get table of ServerID + Lease_End as string to send as mailbody
$mailbody = $_.Group | Format-Table -Property ServerID, Lease_End -AutoSize | Out-String
#You could also create a HTML-table with:
$htmlbody = $_.Group | Select-Object -Property ServerID, Lease_End | ConvertTo-HTML -Property Name, Length
#Send mail
#Send-MailMessage -To $to -Subject "Lease-status" -Body $mailbody -From "mysupersecret#mail.com"
}

PowerShell finding duplicates in CSV and outputting different header

I guess the question is in the title.
I have a CSV that looks something like
user,path,original_path
I'm trying to find duplicates on the original path, then output both the user and original_path line.
This is what I have so far.
$2 = Import-Csv 'Total 20_01_16.csv' | Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } | fl Group | out-string -width 500
This gives me the duplicates in Original_Path. I can see all the required information but I'll be danged if I know how to get to it or format it into something useful.
I did a bit of Googleing and found this script:
$ROWS = Import-CSV -Path 'Total 20_01_16.csv'
$NAMES = #{}
$OUTPUT = foreach ( $ROW in $ROWS ) {
IF ( $NAMES.ContainsKey( $ROW.Original_path ) -and $NAMES[$ROW.original_path] -lt 2 )
{ $ROW }
$NAMES[$ROW.original_path] += 1 }
Write-Output $OUTPUT
I'm reluctant to use this because, well first I have no idea what it's doing. So little of the makes any sense to me, I don't like using scripts I can't get my head around.
Also, and this is the more important part, it's only giving me a single duplicate, it's not giving me both sets. I'm after both offending lines, so I can find both users with the same file.
If anyone could be so kind as to lend a hand I'd appreciate it.
Thanks
It depends on the output format you need, but to build on what you already have we can use this to show the records in the console:
Import-Csv 'Total 20_01_16.csv' |
Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } |
Foreach-Object { $_.Group } |
Format-Table User, Path, Original_path -AutoSize
Alternatively, use this to save them in a new csv-file:
Import-Csv 'Total 20_01_16.csv' |
Group-Object -Property Original_path |
Where-Object { $_.count -ge 2 } |
Foreach-Object { $_.Group } |
Select User, Path, Original_path |
Export-csv -Path output.csv -NoTypeInformation

Resources