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

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 "

Related

Powershell - Hashtable collection in new PSObject

I'm started a query for our infrastructure to see how SNMP Service is configured on the servers.
I managed to find a function that would extract just the relevant keys and values (without the PS* properties) from the 3 Registry paths :
HKLM:\SYSTEM\CurrentControlSet\services\SNMP\Parameters\RFC1156Agent
HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ValidCommunities
HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\PermittedManagers
Edit 1: I've copied the wrong Function initially. Also added the missing part where the function is called. I hope this clears out the confusion
Function Get-HashedProperty {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$True)]
[ValidateScript({Test-Path $_})]
[String]$RegPath,
[Parameter(Mandatory=$False)]
[ValidateSet("Json","HashTable")]
[String]$As
)
$Hash = #{}
Get-ItemProperty "$RegPath" |
Get-Member -MemberType NoteProperty |
Where-Object {$_.Name -notlike "PS*"} | Foreach {
$_ | Select-Object -ExpandProperty Name | Foreach {
$Value = Get-ItemProperty "$RegPath" -Name "$_"
$Hash.Add("$_","$($Value."$_")")
}
}
If($As -eq "Json"){
$Hash = $Hash | ConvertTo-Json
}
Return $Hash
}
$mypaths = #("HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\ValidCommunities","HKLM:\SYSTEM\CurrentControlSet\services\SNMP\Parameters\RFC1156Agent","HKLM:\SYSTEM\CurrentControlSet\Services\SNMP\Parameters\PermittedManagers")
$all = $mypaths | foreach { Get-HashedProperty $_ }
The output of $all variable is below.
Name
Value
BO#CA_w3Ad
8
BO#CA_r3Ad
4
sysLocation
sysContact
sysServices
76
one
hp-sim-ap
three
hp-sim-ap
1
localhost
two
hp-sim-ap
What i'm struggling is to get this values in a separate hashtable or PSObject where i have to add also the ServerName and the InstallState of the SNMP Service. Whatever i do i still get the registry values as array.
Server
SNMP-Service
Col1
Col2
MyTestServer
Installed
{BO#CA_w3Ad, BO#CA_r3Ad, sysLocation, sysContact...}
{8, 4, , ...}
I tried in few different ways, but i assume my lack of knowledge makes it hard to understand where i'm doing wrong.
For example
$a = #()
$item = New-Object PSObject -Property #{
Server = $ENV:ComputerName
'SNMP-Service' = (Get-WindowsFeature -Name SNMP-Service | Select InstallState).InstallState
Col1 = #($All.Keys)
Col2 = #($all.Values)
}
$a += $item
$a
}
Expected output should be
Server
SNMP-Service
Name
Value
MyTestServer
Installed
BO#CA_w3Ad
8
MyTestServer
Installed
BO#CA_r3Ad
4
MyTestServer
Installed
sysLocation
MyTestServer
Installed
sysContact
MyTestServer
Installed
sysServices
76
MyTestServer
Installed
one
hp-sim-ap
MyTestServer
Installed
three
hp-sim-ap
MyTestServer
Installed
1
localhost
MyTestServer
Installed
two
hp-sim-ap
Would highly appreciate a bit of help here :(
I suggest refactoring your approach:
Use the intrinsic psobject property to reflect on the properties of the Get-ItemProperty output objects, which allows you to eliminate the PS-prefixed properties.
Then, for each remaining property, construct a [pscustomobject] instance with Name and Value properties, which you can supplement with the desired Server and SNMP-Service properties.
$mypaths |
Get-ItemProperty |
ForEach-Object {
foreach ($prop in $_.psobject.Properties.Where({ $_.Name -notlike 'PS*'})) {
# Construct and output an object with the desired properties.
[pscustomobject] #{
Server = $ENV:ComputerName
# Note: Better to move this call out of the loop and use a cached value.
'SNMP-Service' = (Get-WindowsOptionalFeature -Name SNMP-Service).InstallState
Name = $prop.Name
Value = $prop.Value
}
}
}
This outputs a stream of objects with the desired structure and values, which you can capture in an array by simple assignment ($a = ...).

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

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

How to get server information from VMware

I have access to the VMWare GUI and I can easily export all the columns such as UPtime, IPAddress, Notes, DNS, GuestOs, State, Name and so on.
I want to right a script that can automatically get this information daily. So gar I was only able to get the server name, power state and VMhost. for some reason VMware is making it so hard to extract that information. I used the script below and I thought by adding the columns I mentioned above to this script, I should be able to retireve the data I need. But it doesn't work that way. Can someone please tell me how I can get this information?
Thanks,
Add-PSSnapin vmware.vimautomation.core
Connect-VIServer SERVERNAME
Get-VM|Select Name, VMHost, Id, PowerState
Exit 0
After digging into the system and many hours of research I found the solution. I just wish VMWare would make it easier to retrieve data or at least improve the manual.
The following code creates two files; one with the server information and another one with Uptime information.
Get-VM | select name,VMHost, #{ Name = "IP Addresses"; Expression = { $_.Guest.IPAddress }}, #{ Name = "PowerState"; Expression = { $_.Guest.State }} , #{ Name = "GuestOS"; Expression = { $_.Guest }}, Notes | Export-Csv -Path "HQstat.csv"
Get-Stat -Entity * -Stat sys.uptime.latest -Realtime -MaxSamples 1| Export-Csv -Path "HQtime.csv"
Why not use the views? They have all the information that you need. Code below assumes you are connected to the vCenter.
$vmView = Get-View -ViewType VirtualMachine -Property Name,Config,Guest,Runtime
$hostView = Get-View -ViewType HostSystem -Property Name
$date = Get-Date
Foreach ($vm in $vmView)
{
If ($vm.Runtime.BootTime -ne $null)
{
$dateDiff = $date.Subtract($vmView.Runtime.BootTime)
}
Else
{
$dateDiff = $null
}
foreach ($h in $hostView)
{
If ($vm.Runtime.Host -eq $h.MoRef)
{
$tempHost = $($h.Name)
Break
}
}
$global:Export += #([PSCustomObject]#{
VMName = $($vm.Name)
ID = $($vm.Config.Uuid) #or use $($vm.MoRef)
Host = $tempHost
PowerState = $($vm.Guest.GuestState)
IPAddress = $($vm.Guest.IPAddress)
Notes = $($vm.Config.Annotations)
UptimeMinutes = $($dateDiff.TotalMinutes)
})
$dateDiff = $null
$tempHost = $null
}
$exportFileName = "C:\temp\VMInformation.csv"
$Export | Export-Csv $exportFileName -Force -NoTypeInformation

Indirect reference to a object member is powershell

I have an object :
Get-ItemProperty -Path ("Registry:"+$my_registry_key) -Name $my_entry
But I still have several useless properties. I would like to compare wath is expected to get out (a string) to zero.
Since % operator is maybe a bit too magical... I would like to expend it to make an indirect reference to property instead of a direct (or hard-coded one). What does happens if property does not exists?
( `
( Get-ItemProperty -Path ("Registry:$my_registry_key") `
| need_some_magic -property $my_entry`
) -ne 0 `
)
And expect one boolean (either $false or $true).
Does Powershell have some kind of hash which can be retrieved through variable instead of $_.property.
You can do the following:
Get-ItemProperty -Path ("Registry:"+$my_registry_key) | select -ExpandProperty $my_entry
So:
if($(Get-ItemProperty -Path ("Registry:"+$my_registry_key) | select -ExpandProperty $my_entry) -ne 0) {
...
}