Call a Group Object by Name in ForEach Loop - arrays

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

Related

PowerShell: Add New Value to Array

Using PowerShell I retrieve information from multiple ActiveDirectory groups and output it to a single CSV file. To each row I want to add the name of the group, in addition to the information retrieved. How can I achieve this?
Desired Result:
"CN","Department","co","Company","Group"
"ABCDEF","Media","UK","Global Entertainment","XXX"
Current Result:
"CN","Department","co","Company"
"ABCDEF","Media","UK","Global Entertainment"
My PowerShell code:
#Roles to check membership for
$Roles= #(
'XXX'
'YYY'
)
$FolderName = "C:\"
$OutputAD = #()
foreach ($role in $Roles) {
$GroupMembers = Get-ADGroupMember -identity $role | select name
$GroupMembersArray = $GroupMembers | Foreach {"$($_.Name)"}
$GroupMembersArray | ForEach-Object {
$UserLookup = Get-ADUser -Filter "Name -eq '$_'" -Properties CN,Department,co,Company | Select-Object CN,Department,co,Company
$OutputAD += $UserLookup
}
}
$OutputAD | Export-Csv "$FolderName\Membership $(get-date -Format yyyy-MMM-dd-HH-mm-ss).csv" -NoTypeInformation
Found the solution. In Select-Object you can add a calculated property.
Example: Select-Object CN,Department,co,Company,#{n='Role';e={$role}}

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

How do i compare every PC name from a multiobject array to a single object array

Hi Powershell newbie alert,
I am trying to compare the computer name of my $getADComp function (CN) with the output of $WSUSArr.
I want to know which PC's are in the WSUS but not and the AD and vice versa. I want the PC names to go in 2 diffrent result arrays so i can use these again later
Eventually i want something like this
$separator = "."
$GetWSUSComp = Get-wsuscomputer -UpdateServer $wsus
$GetADComp = Get-ADComputer -Filter * -Property CN, CanonicalName, Description | Select-Object CN, CanonicalName, Description
$WSUSArr = #()
for ($i = 0; $i -lt $GetWSUSComp.Count; $i++){
$WSUSArr += $GetWSUSComp[$i].FullDomainName.split($separator)[0].ToUpper()
}
Compare-Object -ReferenceObject $WSUSArr -DifferenceObject $GetADComp
If there is a more efficient way to do this feel free to make use of another method (hash table, etc.)
I have another array which is called $WSUSArr which contains the names of all the computer that are connected to the WSUS server i will have to compare those two lists with eachother.
Why not go for an array of objects ?
$GetADComp = Get-ADComputer -Filter * -Property CN, DistinguishedName, Description |
Select-Object CN, DistinguishedName, Description
This way you avoid gathering all properties with -Property * where you only want three.
Every property of items in the array can be accessed by using
$GetADComp[$index].CN
$GetADComp[$index].DistinguishedName
$GetADComp[$index].Description
and compared with an array of CN's like
$GetADComp | Where-Object { $_.CN -eq $WSUSArr[$index] }
In order to compare the computer CN's with the objects returned from the Get-ADComputer cmdlet:
1.
Get the computers that are both in AD and in the WSUS array.
If you want this to be a simple string array of just the CN's, do this:
$ADcomputersInWsus = $GetADComp | Where-Object { $WSUSArr -contains $_.CN } | Select-Object -ExpandProperty CN
Without the Select-Object, this will get you an array of objects with three
properties: CN, DistinguishedName, Description.
$ADcomputersInWsus = $GetADComp | Where-Object { $WSUSArr -contains $_.CN }
# An object array like this is perfect for saving as CSV:
# $ADcomputersInWsus | Export-Csv -Path 'X:\ADcomputersInWsus.csv' -NoTypeInformation
2.
Get a list of computers that are in WSUS, but not in AD:
# ($GetADComp).CN returns an string array with just the CN's, just like the $WSUSArr
$WsusComputersNotInAD = $WSUSArr | Where-Object { ($GetADComp).CN -notcontains $_ }
3.
Get a list of AD computers that are not in WSUS:
$ADcomputersNotInWsus = $GetADComp | Where-Object { $WSUSArr -notcontains $_.CN } | Select-Object -ExpandProperty CN
Note, the $WsusComputersNotInAD is derived from the $WSUSArr string array and is therefore also an array of strings, not objects.
To save that to file, either use:
$WsusComputersNotInAD | Out-File -FilePath "C:\XXX\XXX\WSUSCompNotInAD.txt" -Force
Or convert to an object array and use Export-Csv like the other results:
$WsusComputersNotInAD | ForEach-Object { [PsCustomObject]#{'ComputerName' = $_}} |
Export-Csv -Path "C:\XXX\XXX\WSUSCompNotInAD.csv" -NoTypeInformation

Export-Csv Cannot bind argument to parameter 'InputObject' because it is null

I am trying to fetch the AD group members information through power shell. But I am getting the messages as
Export-Csv : Cannot bind argument to parameter 'InputObject' because it is null.
Import-Module ActiveDirectory
$Path = Get-Location
$GroupName = Read-Host "Type the Group Name to check the members in it`t "
foreach ($group in $GroupName) {
$users = #{}
Get-ADUser -Filter '*' -Property Name, DisplayName | ForEach-Object {
$users[$_.DistinguishedName] = $_
}
$MemberCount = (Get-ADGroup $group -Properties Member | Select-Object -Expand Member).Count
Write-Host "`t Total Number of Users/Groups added on this Group : $MemberCount" -BackgroundColor DarkCyan
$Info = Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
ForEach-Object { $users[$_] }
$Info | Export-Csv -NoTypeInformation -Append -Force $Path\new.csv
The error means that $Info contains empty/null values. The most likely reason for their presence is that the group has members that aren't returned by Get-ADUser.
You can avoid the issue by checking for the presence of the key in the $users hashtable:
$Info = Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
ForEach-Object { if ($users.ContainsKey($_) {$users[$_]} }
If you want to further investigate the missing distinguished names you could collect them like this:
$missing = Get-ADGroup $group -Properties Member |
Select-Object -Expand Member |
ForEach-Object { if (-not $users.ContainsKey($_) {$_} }

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.

Resources