PowerShell, PSCustomObject, array format issue - arrays

I am trying to create array made by PSCustomObject elements, but having some issues with it. Here is the simplified example of the code I have:
$FineData = #()
foreach ($Session in $Sessions) {
$Job = Get-VBRTaskSession -Session $Session
$Result = [pscustomobject]#{
Id = $job.Id.Guid
}
$FineData += $Result
}
$FineData
The format it returns:
Id
{19ea68a7-f2c3-4429-bc46-a87b4a295105, 3ebf8568-10ce-4608-bbb2-c80d06874173, 2ec28852-3f5e-477d-8742-872863e41b6d}
The format I want:
Id : 19ea68a7-f2c3-4429-bc46-a87b4a295105
Id : 3ebf8568-10ce-4608-bbb2-c80d06874173
Id : 2ec28852-3f5e-477d-8742-872863e41b6d
Thanks in advance.

It seems like your Get-VBRTaskSession could be returning more than one object, hence an inner loop is required:
$FineData = foreach ($Session in $Sessions) {
foreach($guid in (Get-VBRTaskSession -Session $Session).Id.Guid) {
[pscustomobject]#{
Id = $guid
}
}
}
$FineData
You can then pipe $FineData to Format-List if you want it to be displayed as a list in your console:
$FineData | Format-List
As aside, adding to a fixed collection should be avoided, see this answer for details: Why should I avoid using the increase assignment operator (+=) to create a collection.

Related

values from a foreach loop in a function into an array

I have a function that replaces PackageID in a SCCM task sequence, I would like to capture all those package IDs into a variable, so I would be able to create a report based on that.
The problem is that I already have a foreach loop doing the work, and I can't figure out how to not overwrite the values.
$Driver.PackageID comes from a foreach loop based on $Drivers, which contains
If I run the code I get this as I have Write-Output defined:
Updated code:
function Set-Drivers{
foreach ($Driver in $Drivers) {
Write-Output "Driver Name: $($Driver.Name)"
Write-Output "DriverPackageID: $($Driver.PackageID)"
}
}
$array = #()
$array = Set-Drivers
$hash = [ordered]#{
'DriverName' = $Driver.Name
'DriverID' = $Driver.PackageID
}
$array += New-Object -Typename PSObject -Property $hash
Can someone explain, why I only get the first result in my $array? I can see the values are being overwritten if I run it in debug mode.
Your code is not iterating over the results, but instead only using one of them. This what you intended.
$array = $drivers | foreach {
[ordered]#{
DriverName = $_.Name
DriverID = $_.PackageID
}
}
Your function doesn't return anything. It only writes lines to the console. Then after the function is finished, you create a single object and add that to your array.
Try something like
function Set-Drivers{
$result = foreach ($Driver in $Drivers) {
[PsCustomObject]#{
'DriverName' = $Driver.Name
'DriverID' = $Driver.PackageID
}
}
# output the result
# the comma wraps the result in a single element array, even if it has only one element.
# PowerShell 'flattens' that upon return from the function, leaving the actual resulting array.
,$result
}
$array = Set-Drivers
# show what you've got
$array

Loading two arrays from a CSV

I have a CSV File that has two columns Employee and Manager and I would like to import them into two Variables for use later in my script. However when I run my code it only captures the last data item in the CSV as the previous one is being over written.
$CSVFiles = Import-CSV "C:\T2\EmployeeManager.csv"
ForEach($CSVFile in $CSVFiles)
{
$Employee = ($CSVFile.Employee); $Manager = ($CSVFile.Manager)
}
$Employee = $CSVFiles | Select -Expand Employee
$Manager = $CSVFiles | Select -Expand Manager
That's because you are overwriting the added data each time the loop runs. IN PowerShell the += appends to an object. Try this -
$Employee = #()
$Manager = #()
$CSVFiles = Import-CSV "C:\T2\EmployeeManager.csv"
ForEach($CSVFile in $CSVFiles)
{
$Employee += $CSVFile.Employee; $Manager += $CSVFile.Manager
}
Vivek Kumar Singh's helpful answer explains the problem with your approach well and offers a solution.
Here's a simpler alternative (PSv3+), given that you're loading the entire CSV file into (custom objects) into memory anyway:
$CSV = Import-CSV "C:\T2\Employee.csv"
$Employees = $CSV.Employee # collect the Employee column values across all input rows
$Managers = $CSV.Manager # ditto for Manager
This approach takes advantage of the PSv3+ member-access enumeration feature.
In PSv2, use iRon's helpful solution.
Comparing the performance of the solutions:
The member-enumeration solution in this answer is fastest,
followed by iRon's Select -Expand solution
with Vivek's foreach loop being the slowest by far, not least because use of += to (conceptually) extend an array requires creating a new instance behind the scenes in every iteration.
This ended up being my final coding to accomplish my end goal. I want to thank all the posters I used a little learning from all of you to come to the solution I wanted.
$EmployeeLists = #()
$ManagerLists = #()
$Employees = #()
$Managers = #()
$CSVFiles = Import-CSV "C:\T2\EmployeeManager.csv"
ForEach($CSVFile in $CSVFiles)
{
$EmployeeLists += ($CSVFile.Employee)
}
ForEach($CSVFile in $CSVFiles)
{
$ManagerLists += ($CSVFile.Manager)
}
$Employees += ForEach ($EmployeeList in $EmployeeLists) { Get-ADUser -Properties * -Filter { DisplayName -like $EmployeeList } | Select SamAccountName -ExpandProperty SamAccountName }
$Managers += ForEach ($ManagerList in $ManagerLists) { Get-ADUser -Properties * -Filter { DisplayName -like $ManagerList } | Select SamAccountName -ExpandProperty SamAccountName }
$EmployeeLists
"`n`n`n"
$Employees

Powershell Group Array of Hashes with Duplicates

I have the following dataset [Array of Hashes]:
Assume ID is always unique
$dataset = #(
#{
ID = "1234567891"
Code = "ABC1111"
},
#{
ID = "1234567892"
Code = "ABC1111"
},
#{
ID = "1234567893"
Code = "ABC1112"
},
#{
ID = "1234567894"
Code = "ABC1113"
},
#{
ID = "1234567895"
Code = "ABC1114"
},
#{
ID = "1234567896"
Code = "ABC1111"
}
)
What I am trying to do is to group the following dataset by Code key.
I already tried multiple methods such as piping Group-By, Group-Object, Sort-Object but I'm still not getting the result I want.
What result I am looking to return is a hashtable which looks like so [Or anything similar]:
$groupedDataset = #{
ABC1111 = #("1234567891","1234567892","1234567896")
ABC1112 = #("1234567893")
ABC1113 = #("1234567894")
ABC1114 = #("1234567895")
}
Convert the hash tables to a PSCustomObjects, group it, then assign it to the new hash table:
$groupedDataset = #{}
$dataset |
ForEach-Object { [PSCustomObject]$_ } |
Group-Object -Property Code |
ForEach-Object { $groupedDataset[$_.Name] = $_.Group.ID }
See Get-Help about_Object_Creation for more information about using [PSCustomObject] to create custom objects from hash tables.
To complement Bacon Bits' helpful answer:
There is no strict need to provide custom objects as input to Group-Object; [hashtable] instances can be used as-is, as long as you use a script-block argument to access the entry to group by (PSv3+ syntax):
$ht = #{}
$dataset | Group-Object { $_.Code } | ForEach-Object { $ht[$_.Name] = $_.Group.Id }
Note the use of { $_.Code } in lieu of [-Property] Code; the latter only works with bona fide properties (as opposed to hashtable entries; conversely, however, { $_.Code } works in either scenario, though Code, if applicable, is faster).

Compare Two Objects Properties and Obtain Missing Items

I'm attempting to compare two object properties called "name" and that contain the same type of data, in this case server host names. The objects are not identical AKA do not have the same properties. In short, I'm attempting to compare two lists of server names and determine where (which object) they are missing from.
I'm looking to find the items (server host names) that are missing from each object. When something is found I'm hoping to obtain all related properties for the item in the given object that it was found in. I can do the compare-object successfully, but don't know how to get the results I'm looking for.
I'm thinking two new objects could be created for each, that list the items that were not found in the other object maybe? Or do I somehow reference the previous objects with the output from compare-object and produce some formatted output?
This code currently produces a blank file.
Data Format:
$SNObject:
name,ip,class
server-place.com,10.10.10.10,windows server
$QRObject:
name,date,ip,general,device type
server-place1.com,11.11.11.11,random info,linux server
Code Example:
$compare = compare-object $SNObject $QRObject -property Name |
foreach {
if ($_.sideindicator -eq '<=')
{$_.sideindicator = $PathToQRReport }
if ($_.sideindicator -eq '=>')
{$_.sideindicator = $PathToSNReport}
}
$compare |
select #{l='Value';e={$_.InputObject}},#{l='File';e={$_.SideIndicator}} |
Out-File -FilePath C:\Temp\MissingOutputs1.txt
Ahh... just thought of an alternative that may give you exactly what you're looking for but in a slightly different way:
## Join both arrays into single array and then group it on the property name that has a shared value, 'name'
$all = #()
$group = #($SNObject + $QRObject)
$group | Group-Object -Property name | % {
## Create a custom object that contains all possible properties plus a directionality indicator ('source')
$n = New-Object PSObject -Property #{
'name' = ''
'date' = ''
'ip' = ''
'general' = ''
'platform' = ''
'source' = ''
}
if ($_.Count -eq 1) {
## Loop through the grouped results and determine their source and write properties based off of their source
foreach ($i in $_.Group) {
if (#($i | Get-Member -MemberType NoteProperty | Select-Object -ExpandProperty name) -contains 'date' ) {
## This value came from $QRObject which apparently is the only dataset that contains a date property
$n.source = 'QRObject'
$n.date = $i.date
$n.general = $i.general
$n.platform = $i.'device type'
} else {
## This object does not contain the 'date' property, therefore it came from $SNObject
$n.source = 'SNObject'
$n.platform = $i.class
}
## write out common properties
$n.name = $i.name
$n.ip = $i.ip
## add the custom PSObject back to a master array with all formatted properties
$all += $n
}
}
}
$all | out-whereever-you-want

How to create and populate an array in Powershell based on a dynamic variable?

I've been struggling with this for a couple of days, and I'm not sure how to conquer it. I need to do the following:
Import a csv of users with the following values:
ID, Name, Region
Create an array based on the Region values that I can then use to populate with ID's and Names with that region, ie.
Array_SEA
AA_SCOM, Adam Andrews, SEA
Array_OAK
BB_SCOM, Bob Barker, OAK
Here's the code I've got right now:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist =#()
foreach ($vitem in $list2)
{
$arraylist += New-Object PsObject -Property #{'Array' = "Array_" + $vitem.bu}
}
foreach ($varray in $arraylist)
{
$arr = new-variable -Name $varray
$arr.value += $varray.array
$arr
}
This produces the following error for records with a duplicate regions:
New-Variable: A variable with name '#{Array=Array_SCA}' already exists.
I'm also getting the following when it tries to add values:
Property 'value' cannot be found on this object; make sure it exists and is settable.
I get that I'm not actually creating arrays in the second section, but I'm not sure how to pass the output of the variable to an array name without turning the variable declaration into the array name, if that makes sense.
I've tried the following with hash tables, and it gets closer:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist =#{}
foreach ($vitem in $list2){$arraylist[$vitem.bu] = #()}
foreach ($record in $list2)
{
$arraylist[$vitem.bu] += ($record.SCOMID,$record.Name,$record.BU)
Write-host "Array: "
$arraylist[$vitem.bu]
write-host ""
}
The output on this shows no errors, but it just keeps showing the added fields for all of the records for each iteration of the list, so I don't think that it's actually assigning each unique BU to the array name.
I like the hashtable-approach, but I would finetune it a little. Try:
$list2 = ipcsv .\TSE_Contact_List.csv | sort-object BU
$arraylist = #{}
foreach ($vitem in $list2){
if($arraylist.ContainsKey($vitem.BU)) {
#Array exists, add item
$arraylist[($vitem.BU)] += $vitem
} else {
#Array not found, creating it
$arraylist[($vitem.BU)] = #($vitem)
}
}
#TEST: List arrays and number of entries
$arraylist.GetEnumerator() | % {
"Array '$($_.Key)' has $($_.Value.Count) items"
}
You could also use Group-Object like:
$list2 = ipcsv .\TSE_Contact_List.csv | Group-Object BU
#TEST: List groups(regions) and number of entries
$list2 | % {
"Region '$($_.Name)' has $(#($_.Group).Count) items"
}

Resources