How to get powershell object properties in the same order that format-list does? - powershell

I'm writing some reporting scripts in Powershell and collecting up a summary table of items as a blank object with additional properties added one by one:
$cmClusters = #()
foreach ($Cluster in Clusters) {
$cmCluster = New-Object System.Object
$cmCluster | Add-Member -type NoteProperty -Name VC -Value $strVC
$cmCluster | Add-Member -type NoteProperty -Name Name -Value $Cluster.name
# etc...
$cmClusters += $cmCluster;
}
If I just dump $cmClusters at the end of this, I get a format-list output with the properties in the order that I added them.
However, I was hoping to write a generic "dump this collection of objects to an excel tab" function to produce my report, which will be several similar worksheet tabs from different lists of objects.
That looks like this:
function DumpToExcel($workbook, $tabTitle, $list)
{
$sheet = $workbook.worksheets.add()
$sheet.Name = $tabTitle
$col = 1
$row = 1
$fields = $list[0] | Get-Member -MemberType NoteProperty | Select-Object *
Foreach ($field in $fields) {
$sheet.cells.item($row,$col++) = $field.Name
}
$heading = $sheet.UsedRange
$heading.Font.Bold = $True
$row++
Foreach ($cmCluster in $list) {
$col=1
Foreach ($field in $fields) {
$sheet.cells.item($row,$col++) = $cmCluster.($field.Name)
}
$row++
}
$sheet.UsedRange.EntireColumn.AutoFit() | Out-Null
}
which works, but the property names are now in alphabetical order.
What can I use to get my list of properties in the same order that Format-List does?

Try this:
$fields = $list[0].psobject.properties | select name

Related

Get VID PID from Device ID in Powershell

I am fairly new to PowerShell and I want for a USB key plugged, to retrieve some info. Right now my script is:
Get-WmiObject win32_diskdrive |
ForEach-Object{
$disk = $_
$_.GetRelated('Win32_PnPEntity')|
ForEach-Object{
$pnp = $_
$_.GetRelated('Win32_USBController') |
ForEach-Object{
$usb = $_
[pscustomobject]#{
SerialNumber = $disk.SerialNumber
Model = $disk.Model
Size = $disk.Size
if ($usb.DeviceID -match '.*VID_(?<vid>[0-9A-F]{4})&PID_(?<pid>[0-9A-F]{4}).*') {VID=$matches['vid']; PID=$matches['pid']}
}
}
}
}
The line beginning with
if ($usb.DeviceID -match '.*VID_(?<vid>[0-9A-F]{4})&PID_(?<pid>[0-9A-F]{4}).*') {VID=$matches['vid']; PID=$matches['pid']}
does not work. I want to translate deviceid (which I can get by doing USBDeviceID = $usb.DeviceID) ID in PID UID directly.
It throws the following error
Error with code “Missing = operator after key in hash literal" for the statement "if ($usb.DeviceID -match '.* ...
What am I missing ? many thanks for helping me .
Gerald
This is because the way you intend to add properties to the PsCustomObject is wrong.
Either do this:
$result = [PsCustomObject]#{
SerialNumber = $disk.SerialNumber
Model = $disk.Model
Size = $disk.Size
}
# add items to the object if the condition is true
if ($usb.DeviceID -match '.*VID_(?<vid>[0-9A-F]{4})&PID_(?<pid>[0-9A-F]{4}).*') {
$result | Add-Member -MemberType NoteProperty -Name 'VID' -Value $matches['vid']
$result | Add-Member -MemberType NoteProperty -Name 'PID' -Value $matches['pid']
}
# output the PsCustomObject
$result
or use a Hashtable as temporary storage:
# create a Hastable to temporarily store results in
$hash = [ordered]#{
SerialNumber = $disk.SerialNumber
Model = $disk.Model
Size = $disk.Size
}
# add items to the hash if the condition is true
if ($usb.DeviceID -match '.*VID_(?<vid>[0-9A-F]{4})&PID_(?<pid>[0-9A-F]{4}).*') {
$hash['VID']=$matches['vid']
$hash['PID']=$matches['pid']
}
# next cast to PsCustomObject and output
[PsCustomObject]$hash

Powershell DataTable displaying headers in the wrong order

when i import my csv into my GUI the headers get imported into the wrong order any ideas?
code below
#### Load Data Table Into Gridview
$button2_Click = {
$data= Import-CSV "E:\testGui\_stock_processed2.csv"
$dt = new-object System.Data.DataTable
$columns = $data | Get-Member -MemberType NoteProperty | select -ExpandProperty name |
$columns | %{
[void]$dt.columns.add($_)
}
$data | %{
$currentRow = $_
$dr = $dt.NewRow()
$columns | %{
$dr.$_ = $currentRow.$_
}
$dt.Rows.Add($dr)
}
$DataGridview1.DataSource = $dt
}
please see correct .CSV headers
and the output the GUI is showing
it looks as though on import the headers get put in alphabetical order
Get-Member formatted output orders the members by MemberType and then by Name. If you want the property list based on the input object order, then you can use PSObject.Properties sub-property.
$data.PSObject.Properties | Foreach-Object {
[void]$dt.Columns.Add($_.Name)
}

How would I store my search results into a table using a foreach loop in powershell?

Import-Module <JAMS>
$JAMSHistories = Get-JAMSHistory -Server TESTDUMMY2 -StartDate "01/20/2020" -EndDate "01/24/2020"
$historyTable = #()
foreach($JAMSHistory in $JAMSHistories)
{
$row = New-Object -TypeName PSObject
Write-Host $JAMSHistory.FinalSeverity
if($JAMSHistory.FinalSeverity -match 'Success')
{
$row | Add-Member -NotePropertyName JobSeveritySuccess ($JAMSHistory.FinalSeverity)
}
else {
if ($JAMSHistory.FinalSeverity -match 'Error') {
$row | Add-Member -NotePropertyName JobSeverityError ($JAMSHistory.FinalSeverity) }
} $historyTable += $row
}
$historyTable
You can do the following, which will output an array of objects ($historyTable) with a properties called JobSeveritySuccess and JobSeverityError.
$JAMSHistories = Get-JAMSHistory -Server TESTDUMMY2 -StartDate "01/20/2020" -EndDate "01/24/2020"
$historyTable = foreach ($JAMSHistory in $JAMSHistories) {
$row = "" | Select JobSeveritySuccess,JobSeverityError
if ($JAMSHistory.FinalSeverity -match 'Success') {
$row.JobSeveritySuccess = $JAMSHistory.FinalSeverity
}
elseif ($JAMSHistory.FinalSeverity -match 'Error') {
$row.JobSeverityError = $JAMSHistory.FinalSeverity
}
$row
}
# Output
$historyTable
# Output in Table Format
$historyTable | Format-Table
The problem with this approach is that every object will have JobSeveritySuccess and JobSeverityError properties, and they may be empty. The only time both properties will have data is if $JAMSHistory.FinalSeverity contains Error and Success for the same object. There is probably a better way to do your design if provided more requirements.
When doing this type of exercise, there are always some gotchas. Consider creating a new PSObject or PSCustomObject for loop iteration. Then output that object at the end of the loop code. When collecting the foreach loop output, just set a variable equal to the foreach loop. There's rarely a need to use += to build an array of foreach loop output. There could be some exceptions to this, but most of the time they are good things to consider.

Pass values in different lines to different variables in Powershell

I have a text file (file1.txt) with records in different lines
Variable1,2018
Variable2,Jun
Now in my script I would like to store the values in different variables.
Example, I would like my end result to be
Year=2018
Period=Jun
I tried the below logic but it is not solving the purpose as it is storing both values in a single variable.
Get-Content "file1.txt" | ForEach-Object {
$Var=$_.split(",")
$Year=$Var[1]
$Period=$Var[1]
}
Please help.
You can do Something like this, if you want use variable :
$Object=(Get-Content C:\Temp\test.txt).Replace(',', '=') | ConvertFrom-StringData
$Object.Variable1
$Object.Variable2
You could do it using a hash like so:
$h = #{}
Get-Content "file1.txt" | % {
$var = $_.split(",")
$h[$var[0]] = $var[1]
}
$h['Variable1']
Or you could use an object like this:
$o = New-Object psobject
Get-Content "file1.txt" | % {
$var = $_.split(",")
$o | Add-Member -MemberType NoteProperty -Name $var[0] -Value $var[1]
$h[$var[0]] = $var[1]
}
$o.Variable1
Why not use the new-variable cmdlet?
#get content from your file instead
$str = "Variable1,2018","Variable2,Jun"
foreach ($line in $str)
{
$line = $line.split(",")
New-Variable -name $line[0] -Value $line[1]
}

Output of installed programs with CSV-Export

I've several problems with the output and export of my current project. I'm using the cmdlet Get-RemoteProgram to get the installed Software via Network and registry entries.
This is my code for now:
function Get-RemoteProgram ....
$computername = Import-Csv "C:\data\test\test.csv" |
select -ExpandProperty PCName
$regex = #("Program1|Program2|Program3")
$items = #()
foreach ($computer in $computername) {
if (Test-Connection $computer -ErrorAction SilentlyContinue -Count 1) {
$query = Get-RemoteProgram -ComputerName $computer -Property DisplayVersion |
where {$_.ProgramName -match $regex}
$obj = New-Object -Type PSObject
$obj | Add-Member -Name ("Computername") -Value $computer -MemberType NoteProperty -Force
$maxcount = $query.ProgramName.Count
if ($maxcount -gt 1) {
for ($i=0; $i -lt $maxcount; $i++) {
$progandversion = $query.ProgramName[$i] + $query.DisplayVersion[$i]
$obj | Add-Member -Name ($progandversion) -Value "Available" -MemberType NoteProperty -Force
}
} elseif ($maxcount -eq 1) {
$progandversion = $query.ProgramName + $query.DisplayVersion
$obj | Add-Member -Name ($progandversion) -Value "Available" -MemberType NoteProperty -Force
}
$obj | Add-Member -Name ("ProgrammVersion$i") -Value $query.DisplayVersion[$i] -MemberType NoteProperty -Force
$items += $obj
}
$items | Export-Csv c:\daten\inventur\output_final.csv -Append -Force
Write-Host "$computer has been checked.."
}
The problem I now have is that my script does not list all different programs that I am looking for. It should export the computername and afterwards - in the same line - put out an available if the software is installed or keep it clean if the program was not found.
That's the output I get right now:
#TYPE System.Management.Automation.PSCustomObject
Computername,"Program1","Program2"
Computer1,"Available","Available"
Computer1,"Available","Available"
Computer2,,
Computer1,"Available","Available"
Computer3,,
Computer2,,
Computer1,"Available","Available"
I don't know why the computers are multiple times in the output.
I would like to have it like this:
Computername,Program1,Program2,Program3,Program4
Computer1,Available,,Available,,
Computer2,Available,,,,
Computer3,,,Available,
Computer4,,,,
Can you help me somehow?
Your problem is two-fold. First, you want to update existing data in a CSV, but instead you use -Append when you run Export-CSV. This explains while more than one row with a given ComputerName exists. And second, you are not setting default values for a given ProgramName, and thus no properties for programs that are not found anywhere exists in the output CSV. To resolve your first problem, you need to run Export-CSV without appending to save your entire data set into your CSV file. And to resolve your second problem, you should pre-fill your new PSObjects with properties. Preparation should be done like this:
$programs=get-content "programs.txt" # one name one line, or an array of names in #()
$regex='('+($programs -join ',')+')' # regex out of array
Then in your main cycle you add this line after call to New-Object:
$programs | % { $obj | Add-Member -Name $_ -Value "Not found" -MemberType NoteProperty } # create default values
Should do. Swap "Not found" for an empty string if you desire.
The encoded version in c# installed programs via windows registry
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
namespace SoftwareInventory
{
class Program
{
static void Main(string[] args)
{
//!!!!! Must be launched with a domain administrator user!!!!!
Console.ForegroundColor = ConsoleColor.Green;
StringBuilder sbOutFile = new StringBuilder();
Console.WriteLine("DisplayName;IdentifyingNumber");
sbOutFile.AppendLine("Machine;DisplayName;Version");
//Retrieve machine name from the file :File_In/collectionMachines.txt
//string[] lines = new string[] { "NameMachine" };
string[] lines = File.ReadAllLines(#"File_In/collectionMachines.txt");
foreach (var machine in lines)
{
//Retrieve the list of installed programs for each extrapolated machine name
var registry_key = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (Microsoft.Win32.RegistryKey key = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machine).OpenSubKey(registry_key))
{
foreach (string subkey_name in key.GetSubKeyNames())
{
using (RegistryKey subkey = key.OpenSubKey(subkey_name))
{
//Console.WriteLine(subkey.GetValue("DisplayName"));
//Console.WriteLine(subkey.GetValue("IdentifyingNumber"));
if (subkey.GetValue("DisplayName") != null)
{
Console.WriteLine(string.Format("{0};{1};{2}", machine, subkey.GetValue("DisplayName"), subkey.GetValue("Version")));
sbOutFile.AppendLine(string.Format("{0};{1};{2}", machine, subkey.GetValue("DisplayName"), subkey.GetValue("Version")));
}
}
}
}
}
//CSV file creation
var fileOutName = string.Format(#"File_Out\{0}_{1}.csv", "Software_Inventory", DateTime.Now.ToString("yyyy_MM_dd_HH_mmssfff"));
using (var file = new System.IO.StreamWriter(fileOutName))
{
file.WriteLine(sbOutFile.ToString());
}
//Press enter to continue
Console.WriteLine("Press enter to continue !");
Console.ReadLine();
}
}
}