We all have projects that involve gathering a list of basic information about the computer and user objects in Active Directory. The PowerShell scripts can make these inventory activities less time consuming, and help us to collect information about computers’ models, BIOS versions, processors and registry settings, documenting the existence of the needed registry keys (for example key for Meltdown& Spectre mitigation).
The PowerShell [PSCustomObject] can without difficulty combine information from different WMI classes, gather the computers’ registry settings, and produce one inventory object that can then be passed / worked on or exported to CSV file.
If we add a progress bar to these reporting scripts, and use the Activity, Status, and CurrentOperation parameters, we will achieve the granularity and deeper level of detail into the scripts’ processing and the status of running commands.
I hope that the example presented here will show you the advantages of using PSCustomObject and encourage you to use it in your reporting scripts.
# ================================================================ # Filename: Report-ComputerHardwareInfo.ps1 # Date: Ferbruary, 2018 # Author: Alex Dujakovic # Description: reporting script to collect info about computers' # hardware and Meltdown/Spectre registry key # ================================================================ # VARIABLES - adjust values to your environment # --------------------------------------------------------------- # CSV file to collect / store info about computers $csvFilePath = "C:\PSScripts\Hardware\PC_HardwareAndRegInfo.csv" # Collect info for this Registry path $MeltdownSpectreRegistryPath = "SOFTWARE\Microsoft\Windows\CurrentVersion\QualityCompat" # Collection of the computers for inventory # # Use an OU container for computers' names # --------------------------------------------------------------- $myPcCollection = [ADSI]"LDAP://OU=Computers,OU=Accounts,DC=AlexTest,DC=Local" # Log file to log errors $logFile="C:\PSScripts\Hardware\HardwareAndReg.log" # ================================================================ # Check if .CSV file exist, and create empty one with column names if (!(Test-Path -path $csvFilePath)) { ""|Select Name,PcManufacturer,PcModel,UserName,SerialNumber,BIOSName,Version, SMBIOSBIOSVersion,BIOSManufacturer,Processor,ProcessorModel,ProcessorManufacturer, Key,KeyValue,RegPath | Export-Csv -Path $csvFilePath -NoTypeInformation} $PCcollection = ($myPcCollection.psbase.Children| Where -FilterScript{$_.ObjectCategory -Like "*Computer*"} | Select-Object -ExpandProperty Name) # Computers contained in CSV File will not be pinged again $reachedPCs = (Import-Csv -Path $csvFilePath | Select-Object -ExpandProperty Name) # Creating progress bar properties $Activity = "OBJECTIVE: Collecting Info - Creating Report" $CurFolderText = '"Item: $($CurNumber.ToString().PadLeft($collectionItem.Count.ToString().Length)) of $($collectionItem) "' $CurFolderBlock = [ScriptBlock]::Create($CurFolderText) # ---------------------|START OF FUNCTIONS|---------------------- Function GetRemoteReg($computerName,$state){ Try{ $svcState = (Get-WmiObject -Class Win32_Service -ComputerName $computerName ` -Filter "Name = 'RemoteRegistry'" -ErrorAction Stop).State Write-Host "$($ComputerName) - State: $svcState" -ForegroundColor DarkGray Switch($state){ "START"{ Switch($svcState){ "Stopped"{(Get-WmiObject -ComputerName $computerName Win32_Service ` -Filter "Name = 'RemoteRegistry'").StartService() Write-Host "$($ComputerName) - Started RemoteRegisty Service" -ForegroundColor DarkGray Break } "Running"{Write-Host "$($ComputerName) - RemoteRegisty Service Running" ` -ForegroundColor DarkGray; Break} } Break } "STOP"{ Switch($svcState){ "Stopped"{Write-Host "$($ComputerName) - RemoteRegisty Service Stopped" ` -ForegroundColor DarkGray; Break} "Running"{(Get-WmiObject -ComputerName $computerName Win32_Service ` -Filter "Name = 'RemoteRegistry'").StopService() Write-Host "$($ComputerName) - Stopped RemoteRegisty Service" -ForegroundColor DarkGray Break } } Break } } } Catch{ Write-Host "$($ComputerName) - RemoteRegisty Service Error: ` $($_.Exception.GetType().FullName) - $($_.Exception.Message)" ` -ForegroundColor DarkGray } } Function Get-RegistryPath { Param([Management.ManagementObject]$Reg,[int64]$Hive,[string]$regpath) Function Get-RegistryValue { Param([Management.ManagementObject]$Reg, [int64]$Hive, [string]$regitem, [string]$value, [int32]$iType) $obj=New-Object PSObject switch ($iType) { 1 {$data=($reg.GetStringValue($Hive,$regitem,$value)).sValue} 2 {$data=($reg.GetExpandedStringValue($Hive,$regitem,$value)).sValue} 3 {$data="Binary Data"} 4 {$data=($reg.GetDWordValue($Hive,$regitem,$value)).uValue} 7 {$data=($reg.GetMultiStringValue($Hive,$regitem,$value)).sValue} default {$data="Unable to retrieve value"} } Add-Member -inputobject $obj -membertype "NoteProperty" -name Key -value $value Add-Member -inputobject $obj -membertype "NoteProperty" -name KeyValue -value $data Add-Member -inputobject $obj -membertype "NoteProperty" -name RegPath -value $regitem Add-Member -inputobject $obj -membertype "NoteProperty" -name Hive -value $hive Add-Member -inputobject $obj -membertype "NoteProperty" -name KeyType -value $iType write $obj } # end of Get-RegistryValue Subfunction # obtain values of current registry key $values=$Reg.enumValues($Hive,$regpath) if ($values.snames.count -gt 0) { for ($i=0;$i -lt $values.snames.count;$i++) { $iType = $values.types[$i] $value = $values.snames[$i] Get-RegistryValue $Reg $Hive $regpath $value $iType } } $keys=$Reg.EnumKey($Hive,$regpath) # enumerate any sub keys if ($keys.snames.count -gt 0) { foreach ($item in $keys.snames) { #recursively call this function Get-RegistryPath $Reg $hive "$regpath\$item" } } } # end of Get-RegistryPath function Function Get-HardwareInfo($ComputerName){ # define reg for remote computer [WMIClass]$Reg = "\\$($computerName)\root\default:StdRegProv" # define registry hive as a numeric constant $HKLM=2147483650 # define registry path $registryPath = "$($MeltdownSpectreRegistryPath)" #Get computer system Information $compSystem = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $ComputerName #Get CPU Information $CPUInfo = Get-WmiObject -Class Win32_Processor -ComputerName $ComputerName #Get BIOS Information $BIOS = Get-WmiObject -Class Win32_BIOS -ComputerName $ComputerName # start remote computer's registry GetRemoteReg -computerName $ComputerName -state "START" | Out-Null # obtain infor from remote registry $regQuery = (Get-RegistryPath -Reg $Reg -Hive $HKLM -regpath "$($registryPath)" | Select Key,Keyvalue,RegPath) # stop remote computer's registry GetRemoteReg -computerName $ComputerName -state "STOP" | Out-Null # create PowerShell custom object $OS = [PSCustomObject]@{ # Date = Get-Date Name = $ComputerName PcManufacturer = $compSystem.Manufacturer PcModel = $compSystem.Model UserName = $compSystem.UserName # SerialNumber = $BIOS.SerialNumber BIOSName = $BIOS.Name Version = $BIOS.Version SMBIOSBIOSVersion = $BIOS.SMBIOSBIOSVersion BIOSManufacturer = $BIOS.Manufacturer # Processor = $CPUInfo.Name ProcessorModel = $CPUInfo.Description ProcessorManufacturer = $CPUInfo.Manufacturer # Key = $regQuery.Key KeyValue = $regQuery.KeyValue RegPath = $regQuery.RegPath } $OS | Export-Csv -Path $csvFilePath -NoTypeInformation -Append } # -------------|END OF FUNCTIONS|----------------- $newQueryCollection = (Compare-Object -ReferenceObject ` $PCcollection -DifferenceObject $reachedPCs | Where-Object -FilterScript {$_.SideIndicator -eq "<="} | Select-Object -ExpandProperty InputObject | Sort InputObject) $pcCount = 1 $CurNumber = 0 $collectionItem = $newQueryCollection.Count foreach ($Computer In $newQueryCollection){ $CurNumber++ $CurFolderpPercent = $CurNumber / $collectionItem * 100 $Task = "Current Task: processing $($Computer)" Write-Progress -Id 1 -Activity $Activity -Status (& $CurFolderBlock) ` -CurrentOperation $Task -PercentComplete $CurFolderpPercent Write-Host "$($Computer) - Try Ping" -ForegroundColor White If (Test-Connection -ComputerName $Computer -ea silentlycontinue -Count 1) { Write-Host "$($Computer) - Ping Successful" -ForegroundColor Green try{ $ErrorActionPrefference = "STOP" Get-HardwareInfo -ComputerName $Computer | Out-Null } catch [System.Management.Automation.RuntimeException] { # Any WMI related service is disabled/not responding. Write-Host "$($Computer) - No RPC Server" -ForegroundColor Yellow } catch{ "$($Computer) - Error: $($_.Exception.GetType().FullName) - $($_.Exception.Message)" | Out-file -FilePath $logFile -Encoding ascii -Append } } else{ Write-Host "$($Computer) - Ping Failed" -ForegroundColor Red } $pcCount ++ } Write-Host "Script finished execution" -ForegroundColor Yellow
To reiterate the advantages of PSCustomeObject:
• combine information from different sources (WMI classes, Registry settings, functions)
• preserve order in display
• quicker processing
The script with name Report-ComputerHardwareInfo.ps1 could be downloaded from the download/PowerShell section of this site.
|