Populating GUI listview on a single line - winforms

Trying to workout why the below function (executed from a GUI button) does not place all the output on the same line in the GUI listview. Each command in the function places the output one line down in the list view from the previous command. Tried piping commands, variables and arrays without success.
Function Get-MailboxSizeQuotasTool {
Get-Mailbox $WPFinputbox.Text |
Select-Object #{Name='Display Name';Expression={$_.DisplayName}},
#{Name='Prohibit Send Quota';Expression={$_.ProhibitSendQuota}},
#{Name='Use Database Defaults';Expression={$_.UseDatabaseQuotaDefaults}},
#{Name='Archive Quota';Expression={$_.ArchiveQuota}}
Get-MailboxStatistics $WPFinputbox.Text |
Select-Object #{Name='Mailbox Size';Expression={$_.TotalItemSize}}
Get-MailboxStatistics $WPFinputbox.Text -Archive |
Select-Object #{Name='Archive Size';Expression={$_.TotalItemSize}}
}
$WPFokbutton.Add_Click({
$WPFlist.Items.Clear()
Start-Sleep -Milliseconds 840
Get-MailboxSizeQuotasTool -Identity $WPFinputbox.Text |
% {$WPFlist.AddChild($_)}
})

Your function outputs 3 different objects with different properties. You need to consolidate the information in a single object:
Function Get-MailboxSizeQuotasTool {
$mailbox = Get-Mailbox $WPFinputbox.Text
New-Object -Type PSObject -Property #{
'Display Name' = $mailbox.DisplayName
'Prohibit Send Quota' = $mailbox.ProhibitSendQuota
'Use Database Defaults' = $mailbox.UseDatabaseQuotaDefaults
'Archive Quota' = $mailbox.ArchiveQuota
'Mailbox Size' = Get-MailboxStatistics $WPFinputbox.Text |
Select-Object -Expand TotalItemSize
'Archive Size' = Get-MailboxStatistics $WPFinputbox.Text -Archive |
Select-Object -Expand TotalItemSize
}
}

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
}

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"
}

Add new property based on a different objects property

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.

local users and groups output to a file

I have a script that shows all the local users and their associated groups. However, I'm trying to output the results into a text file and that's where the script goes wrong, because it's not giving me the same results I'm receiving from the output window. For example, the code I have reads:
$LogFile = Test-Path C:\Users\FredAslami\Downloads\Test.txt
$LocalUsers = [ADSI]"WinNT://$env:COMPUTERNAME"
if ($LogFile) {
$LocalUsers.Children | where {$_.SchemaClassName -eq 'user'} | Foreach-Object {
$groups = $_.Groups() | Foreach-Object {
$_.GetType().InvokeMember("Name", 'GetProperty', $null, $_, $null)
}
$_ | Select-Object #{n='UserName';e={$_.Name}},
#{n='Groups';e={$groups -join ';'}}
}
Write-Host "Got User Groups Info"
Out-File -FilePath C:\Users\FredAslami\Downloads\Test.txt `
-InputObject $LocalUsers -Append
Write-Host "Added info to text"
}
$LocalUsers.Dispose()
When I run that the text in the file will read
distinguishedName :
Path : WinNT://R68-CUSTOM-01
I have also tried using Add-Content, but that doesn't work either. It will add something like:
System.DirectoryServices.DirectoryEntry
I also, tried to debug using Write-Host after it retrieves the local users and group info and another Write-Host after it writes the results into the text file and noticed that it's writing the results before it gathered all the info. So I tried using the Start-Sleep, and that didnt seem to work.
On the second line you have $LocalUsers = [ADSI]"WinNT://$env:COMPUTERNAME". You never assigned it a different value, so that's what you're seeing as your output.
I would recommend piping your Select-Object statement to Export-Csv. Much easier and cleaner.
You get different results in screen and file output, because you're doing different things with your data. The pipeline starting with $localUsers.Children builds a list of the user objects and their group memberships and echoes that to the screen, but you don't do anything else with that data. Instead you're writing the unmodified variable $localUsers to the output file.
If you want tabular data to go both to the console and a file, I'd suggest using Write-Host for the console output, and Export-Csv for the file output:
$LocalUsers.Children | where {$_.SchemaClassName -eq 'user'} | Foreach-Object {
$groups = $_.Groups() | Foreach-Object {
$_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null)
}
$o = New-Object -Type PSObject -Property #{
'UserName' = $_.Name
'Groups' = $groups -join ';'
}
Write-Host $o
$o
} | Export-Csv 'C:\Users\FredAslami\Downloads\Test.txt' -NoType
If you want the output to go to the success output stream instead of the console, you could capture the result in a variable and output that in two different ways:
$users = $LocalUsers.Children | where {
$_.SchemaClassName -eq 'user'
} | Foreach-Object {
$groups = $_.Groups() | Foreach-Object {
$_.GetType().InvokeMember('Name', 'GetProperty', $null, $_, $null)
}
New-Object -Type PSObject -Property #{
'UserName' = $_.Name
'Groups' = $groups -join ';'
}
}
$users
$users | Export-Csv 'C:\Users\FredAslami\Downloads\Test.txt' -NoType

Resources