Output of installed programs with CSV-Export - powershell

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();
}
}
}

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

CSV misalignment when reading in powershell

I have a CSV with a lot of headers. It ranges from Column A to AZ. I need to read only the first column of the CSV.
My issue is the last column (comments) bleeds into the first column when opened in notepad++ and it appears to show that it is apart of Column one (host name). When I run my code it reads it as notepad++ does.
I originally convert this from an .xls to a CSV file, is there a way to ensure the last column does not wrap around?
#region - Convert to CVS
$strFileName = "C:\working\Server Count & Status Check ARC 0219183.xlsx"
$strSheetName = 'SCI$'
$strProvider = "Provider=Microsoft.ACE.OLEDB.12.0"
$strDataSource = "Data Source = $strFileName"
$strExtend = "Extended Properties='Excel 8.0;HDR=Yes;IMEX=1';"
$strQuery = "Select * from [$strSheetName]"
$objConn = New-Object
System.Data.OleDb.OleDbConnection("$strProvider;$strDataSource;$strExtend")
$sqlCommand = New-Object System.Data.OleDb.OleDbCommand($strQuery)
$sqlCommand.Connection = $objConn
$objConn.open()
$da = New-Object system.Data.OleDb.OleDbDataAdapter($sqlCommand)
$dt = New-Object system.Data.datatable
$da.fill($dt)
$dt | Export-Csv "C:\working\Server Count & Status Check ARC 0219183.csv" -
NoTypeInformation
$objConn.close()
#endregion
Original Spreadsheet
After trying to gather first column(hostname)
$array=#{}
$ServerStatus = Get-Content "C:\Users\user\Desktop\Scripts\Linux Server
Compare\Server Count & Status Check ARC 0219183.csv" | select -skip 1 |
ConvertFrom-Csv -Header "HostName","Business Application","Technical
Function","Location","Environment","Day Number","Slot
Number","SlotTime","Description","DNS Name","Key Server","Host Name","Public
IP","Server Type","Build Date","Builder","Product Name","Serial
No","Enclosure","Bay","CPU Model","CPU Socket","CPU Cores","Logical
CPU","Memory","Up Time","Kernel Version","OS Type","Version","Patch
level","Multipath Disks?","Multipath Checker?","Customized Fsck?","Splx
Installed?","Splx Running?","Suse Manager Status","RHN Running ?","RHN
Autostart?","Installed Kernel","Available Kernel in
/boot","Hardened","Syslog
Config correct ?","Syslog Running ?","Patrol Running?","Splunk
Installed?","Splunk Running?","Puppet Installed?","Puppet
Running?","Centrify
Version","VM Tools Installed?","VM Tools Running?","Comments"
$ServerStatusObj = new-object Object
$ServerStatusObj | add-member -membertype NoteProperty "Name" -value ""
$ServerStatusObj | add-member -membertype NoteProperty "Origin" -value ""
$array=#{name = $ServerStatus.HostName}
foreach ($name in $array.name){
if($name -ne $null){
$Value = $name
if($name -ne $null){
$ServerStatusObj.name =$Value
$ServerStatusObj.Origin = " Linux Servers"
} #End of IF
else {Write-Output "null"}
Write-Output $ServerStatusObj | export-csv
"C:\Users\user\Desktop\Scripts\Linux Server Compare\ServerStatusParse.csv" -notypeinformation -Append
}

How to enumerate Shadow Copies of a given file or folder?

So i'm essentially trying to retrieve the information in the screenshot but with PowerShell. Kind of getting stumped at the first hurdle.
Best i can find is https://superuser.com/questions/643536/how-to-find-and-open-previous-versions-of-a-folder-programmatically-using-power
But it's not really suited.
Been reading up on https://msdn.microsoft.com/en-us/library/aa393625(v=vs.85).aspx
But I'm not able to make much sense of it. Is that the right path to be going down?
There are a few of steps in PowerShell to get to browsing shadow copies.
First, below code will display a list of drives and their shadow copies
$shadowStorageList = #();
$volumeList = Get-WmiObject Win32_Volume -Property SystemName,DriveLetter,DeviceID,Capacity,FreeSpace -Filter "DriveType=3" | select #{n="DriveLetter";e={$_.DriveLetter.ToUpper()}},DeviceID,#{n="CapacityGB";e={([math]::Round([int64]($_.Capacity)/1GB,2))}},#{n="FreeSpaceGB";e={([math]::Round([int64]($_.FreeSpace)/1GB,2))}} | Sort DriveLetter;
$shadowStorages = gwmi Win32_ShadowStorage -Property AllocatedSpace,DiffVolume,MaxSpace,UsedSpace,Volume |
Select #{n="Volume";e={$_.Volume.Replace("\\","\").Replace("Win32_Volume.DeviceID=","").Replace("`"","")}},
#{n="DiffVolume";e={$_.DiffVolume.Replace("\\","\").Replace("Win32_Volume.DeviceID=","").Replace("`"","")}},
#{n="AllocatedSpaceGB";e={([math]::Round([int64]($_.AllocatedSpace)/1GB,2))}},
#{n="MaxSpaceGB";e={([math]::Round([int64]($_.MaxSpace)/1GB,2))}},
#{n="UsedSpaceGB";e={([math]::Round([int64]($_.UsedSpace)/1GB,2))}}
# Create an array of Customer PSobject
foreach($shStorage in $shadowStorages) {
$tmpDriveLetter = "";
foreach($volume in $volumeList) {
if($shStorage.DiffVolume -eq $volume.DeviceID) {
$tmpDriveLetter = $volume.DriveLetter;
}
}
$objVolume = New-Object PSObject -Property #{
Volume = $shStorage.Volume
AllocatedSpaceGB = $shStorage.AllocatedSpaceGB
UsedSpaceGB = $shStorage.UsedSpaceGB
MaxSpaceGB = $shStorage.MaxSpaceGB
DriveLetter = $tmpDriveLetter
}
$shadowStorageList += $objVolume;
}
for($i = 0; $i -lt $shadowStorageList.Count; $i++){
$objCopyList = Get-WmiObject Win32_ShadowCopy | Where-Object {$_.VolumeName -eq $shadowStorageList[$i].Volume} | select DeviceObject, InstallDate
$shadowStorageList[$i] | add-member Noteproperty shadowcopies $objCopyList
$shadowStorageList[$i]
}
Sample output:
AllocatedSpaceGB : 9.17 DriveLetter : F: Volume :
\?\Volume{6c974bfe-0525-11e7-80bf-0050568007f5}\ MaxSpaceGB :
15 UsedSpaceGB : 8.46 shadowcopies :
{#{DeviceObject=\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy39;
InstallDate=20170902070009.648986+600}, #{DeviceObject=\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy40;
InstallDate=20170903070009.902376+600}, #{DeviceObject=\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy41;
InstallDate=20170904070016.340573+600}, #{DeviceObject=\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy42;
InstallDate=20170904120031.644419+600}...}
AllocatedSpaceGB : 6.28 DriveLetter : C: Volume :
\?\Volume{4c22f9da-2b50-11e6-80b3-806e6f6e6963}\ MaxSpaceGB :
6.96 UsedSpaceGB : 5.78 shadowcopies : {#{DeviceObject=\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy3;
InstallDate=20170921070020.298687+600},
#{DeviceObject=\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy4;
InstallDate=20170921120026.126738+600},
#{DeviceObject=\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy5;
InstallDate=20170922070025.309517+600},
#{DeviceObject=\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy6;
InstallDate=20170922120004.852824+600}...}
To browse a shadow copy (example GLOBALROOT\Device\HarddiskVolumeShadowCopy6), you need to create a symbolic link to it (windows shortcut) which you can then browse in Windows explorer.
Example code below:
# Load assembly to create symlink
try {
$null = [mklink.symlink]
}
catch {
Add-Type #"
using System;
using System.Runtime.InteropServices;
namespace mklink
{
public class symlink
{
[DllImport("kernel32.dll")]
public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags);
}
}
"#
}
# create symlink
[mklink.symlink]::CreateSymbolicLink('symlink path (example C:\temp\link1)', '\\?\GLOBALROOT\Device\HarddiskVolumeShadowCopy4\', 1);
Accessing Volume Shadow Copy (VSS) Snapshots from powershell has some further information.
There is also a utility called vssadmin which appears to be the native utility. This link uses vssadmin and select-string to get the information. https://p0w3rsh3ll.wordpress.com/2014/06/21/mount-and-dismount-volume-shadow-copies/
Though it may not be an exact solution to the OP's question I'm adding this to several related posts in the hopes it will help someone else, who, like me, needed a way to list all snapshots off a remote path. Searching for this was agony and I was about to give up until I found the answer.
I searched and searched for the ability to list previous versions programmatically.. could not find a solution for viewing previous versions on an SMB / CIFS share. Volrest, vssadmin, alphaVss, etc.. round and round.. even win32_shadowCopy failed, because our target machines are netapps. Nothing worked.
Then I fount this posting says they can use SMB commands in Perl to view them. https://mark.clow.es/?blog/2018/02/listing-shadow-copies
If Perl can do it, surely some winAPI could also. FSCTL_SRV_ENUMERATE_SNAPSHOTS is the SMB command needed. https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-smb/bffc70f9-b16a-453b-939a-0b6d3c9263af
Finally found this genius ( God bless kenjiuno ) who created a dll for .NET: https://github.com/HiraokaHyperTools/LibEnumRemotePreviousVersion
After adding reference to kenjiuno's dll, I called it:
Dim s() as String = LibEnumRemotePreviousVersion.PreviousversionOnRemote("\\server\share")
it returned all previous versions as #GMT-blablabla. Then all you need to do is append the one you want to the end of your UNC path.
No mklink, no mapping drives, none of it.. everything works exactly and as simply as it should have if Microsoft would have pulled their head out and put this in .Net themselves.
Shadow copies are not stored on a per-folder basis. It's a per-volume basis. You can exclude things from being shadow-copied on that volume by setting registry keys in HKLM\system\CurrentControlSet\Control\BackupRestore\FilesNotToSnapshot. You'll see examples there already. Be aware of the "/s" parameter which indicates to recurse sub-directories. Also note that you can't put wildcards in the middle of the path. They can only be at the end. Below is my script to enumerate the copies with their associated information.
$SnapshotState=#("VSS_SS_UNKNOWN","VSS_SS_PREPARING","VSS_SS_PROCESSING_PREPARE","VSS_SS_PREPARED","VSS_SS_PROCESSING_PRECOMMIT","VSS_SS_PRECOMMITTED","VSS_SS_PROCESSING_COMMIT","VSS_SS_COMMITTED","VSS_SS_PROCESSING_POSTCOMMIT","VSS_SS_PROCESSING_PREFINALCOMMIT","VSS_SS_PREFINALCOMMITTED","VSS_SS_PROCESSING_POSTFINALCOMMIT","VSS_SS_CREATED","VSS_SS_ABORTED","VSS_SS_DELETED","VSS_SS_POSTCOMMITTED","VSS_SS_COUNT")
$Volumes=Get-Volume
$ShadowProvider=Get-WmiObject -Namespace "root\cimv2" -Class "Win32_ShadowProvider" | Select-Object -Property ID,Name
$ShadowOn=Get-WmiObject -Namespace "root\cimv2" -Class "Win32_ShadowOn" | Select-Object -Property Dependent,Antecedent
$ShadowOn | ForEach-Object { $_.Dependent=$_.Dependent.Replace("Win32_ShadowCopy.ID=",""); $_.Antecedent=$_.Antecedent.Replace("Win32_Volume.DeviceID=",""); $_.Dependent=$_.Dependent.Replace('"',""); $_.Antecedent=$_.Antecedent.Replace('"',""); $_.Antecedent=$_.Antecedent.Replace("\\","\") }
$ShadowCopy=Get-WmiObject -Namespace "root\cimv2" -Class "Win32_ShadowCopy"
$ShadowCopy=#($ShadowCopy)
for ($i=0; $i -lt $ShadowCopy.Count; $i++) {
$DiffVolume=$ShadowOn | Where-Object { $_.Dependent -eq $ShadowCopy[$i].ID } | Select-Object -ExpandProperty Antecedent
$DiffVolume=$Volumes | Where-Object { $_.Path -eq $DiffVolume } | Select-Object -ExpandProperty DriveLetter
$ShadowCopy[$i] | Add-Member -MemberType NoteProperty -Name "DiffVolume" -Value $DiffVolume
$DriveLetter=$Volumes | Where-Object { $_.Path -eq $ShadowCopy[$i].VolumeName } | Select-Object -ExpandProperty DriveLetter
$ShadowCopy[$i] | Add-Member -MemberType NoteProperty -Name "Volume" -Value $DriveLetter
$ShadowCopy[$i] | Add-Member -MemberType NoteProperty -Name "strState" -Value $SnapshotState[$ShadowCopy[$i].State]
$ShadowCopy[$i] | Add-Member -MemberType NoteProperty -Name "CreateDate" -Value ([System.Management.ManagementDateTimeConverter]::ToDateTime($ShadowCopy[$i].InstallDate))
$ShadowCopy[$i].PSObject.Properties.Remove("VolumeName")
$ShadowCopy[$i] | Add-Member -MemberType NoteProperty -Name "Provider" -Value ($ShadowProvider | Where-Object { $_.ID -eq $ShadowCopy[$i].ProviderID } | Select-Object -ExpandProperty Name)
$ShadowCopy[$i].PSObject.Properties.Remove("ProviderID")
}
$ShadowCopy | Select-Object Count,CreateDate,Volume,DiffVolume,strState,MaxSpace,UsedSpace,AllocatedSpace,Persistent,Differential,ClientAccessible,NoAutoRelease,NoWriters,ExposedLocally,ExposedRemotely,NotSurfaced,Transportable,Provider | Sort-Object -Property Count
After many tries and Copy-Pasting code samples from all over the internet,
I come up with this solution, maybe somebody will find it useful.
function Get_Snapshots
{
param (
[ValidateScript({
if (-Not ($_ | Test-Path -PathType Container))
{
throw "Not a Diretory"
}
return $true
})]
[System.IO.FileInfo]$Path
)
if (-not ("Snapshot.GetSnapshot" -as [type]))
{
Add-Type -Language VisualBasic #"
Imports System
Imports System.Collections.Generic
Imports System.IO
Imports System.Runtime.InteropServices
Namespace Snapshot
Public Class GetSnapshot
<StructLayout(LayoutKind.Sequential)> Structure IOSTATUSBLOCK
Public Status As Integer
Public Information As Integer
End Structure
Declare Function NtFsControlFile Lib "ntdll" (ByVal FileHandle As Microsoft.Win32.SafeHandles.SafeFileHandle, ByVal Evennt As IntPtr, ByVal ApcRoutine As IntPtr, ByVal ApcContext As IntPtr, ByRef IoStatusBlock As IOSTATUSBLOCK, ByVal FsControlCode As Integer, ByVal InputBuffer() As Byte, ByVal InputBufferLength As Integer, ByVal OutputBuffer() As Byte, ByVal OutputBufferLength As Integer) As IntPtr
Declare Auto Function CreateFile Lib "kernel32.dll" (ByVal lpFileName As String,ByVal dwDesiredAccess As Integer, ByVal dwShareMode As Integer, ByVal lpSecurityAttributes As IntPtr, ByVal dwCreationDisposition As Integer, ByVal dwFlagsAndAttributes As Integer, ByVal hTemplateFile As IntPtr)As Microsoft.Win32.SafeHandles.SafeFileHandle
End Class
End Namespace
"#
}
try{
$NtStatus = [System.Byte[]]::CreateInstance([System.Byte], 64000)
$NtStatusClean = [System.Byte[]]::CreateInstance([System.Byte], 64000)
$Filehandle = [Snapshot.GetSnapshot]::CreateFile([ref]$Path, 129, 1, [System.IntPtr]::Zero, 3, 33554432, [System.IntPtr]::Zero)
$objIOStatusBlock = [Activator]::CreateInstance([Snapshot.GetSnapshot+IOSTATUSBLOCK])
$intServerResponse = [Snapshot.GetSnapshot]::NtFsControlFile($Filehandle, [System.IntPtr]::Zero, [System.IntPtr]::Zero, [System.IntPtr]::Zero, [ref]$objIOStatusBlock, 1327204, $null, 0, $NtStatus, $NtStatus.Length);
$Filehandle.Dispose()
if ($intServerResponse -ne 0)
{
return ("Error request was not successfull: " + $intServerResponse)
}
[System.Array]::Copy($NtStatus, 12, $NtStatusClean, 0, ($NtStatusClean.Length - 12))
$Snapshots = ([System.Text.Encoding]::Unicode.GetString($NtStatusClean) -split "`0" | Where-Object { $_ -ne "" })
$Snapshots
}
catch
{
$Error[0]
}
}
Example:
Get_Snapshots h:\P12
#GMT-2021.08.05-08.30.01
#GMT-2021.08.04-14.00.08
After that I can do a "get-childitem h:\P12\#GMT-20xxx "

create an object from returned variables of a foreach powershell

I am trying to create a script that receives a list of computers and for-each computer I need to create a process, after running the process, I have many variables that y need to append to a an object
How can I append to the object, the returned info of the pcs?
#input variable that the foreach need to process
$Machines = "pc1,pc2,pc3,pc4,pc5,pc6,pc7,pc8,pc9,pc0"
#create empty object
$pcNull
$MachineNull
$usersNull
$object= New-Object Object
$object | Add-Member NoteProperty propiedad $MachineNull
$object | Add-Member NoteProperty users $usersNull
$object | Add-Member NoteProperty computer $pcNull
$object | Add-Member NoteProperty error $false
foreach ($Machine in $Machines )
{
#where i make a process foreach computer and there are variables that are returned
{
}
###############################
#where i am trying to append to the created object the returned variables
#append to propiedad property
$object.propiedad = $object.propiedad = $MachineNull
$object
#append to users property
$object.users = $object.users = $false
$object
#append to computers property
$object.computer = $object.computer = $Machine
$object
}
$object
You're probably better off creating multiple objects, one per computer, inside the foreach loop.
# Loop through machines, assign all output to $Objects variable
$Objects = foreach ($Machine in $Machines)
{
#where i make a process foreach computer and there are variables that are returned
{
}
###############################
#where i am trying to append to the created object the returned variables
# Define the properties that the object should have in a hashtable
$ObjectProperties = #{
# Assuming you've assigned something to $Propriedad, $Users and $ErrorState above
Propiedad = $Propriedad
Users = $Users
Computer = $Machine
Error = $ErrorState
}
# Now create an object.
# When we just drop it in the pipeline like this, it gets assigned to $Objects
New-Object psobject -Property $ObjectProperties
}
Now you can create HTML from your objects with ConvertTo-Html:
$Objects | ConvertTo-Html -As Table -Head "<title>Kimo's report</title>"
If you want to test it, you'll need to change $Machines to:
"pc1","pc2","pc3","pc4","pc5","pc6","pc7","pc8","pc9","pc0"
If you want to save this as a .ps1 script file and be able to pass the computer names as arguments, add a param() block at the top:
param([string[]]$Machines)
Now, if you save the script as "KimosReporter.ps1", you can run it against any computer like this:
PS C:\>.\KimosReporter.ps1 -Machines "pc1","pc6","pc9"

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

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