|
| 1 | +<# |
| 2 | +.SYNOPSIS |
| 3 | + Interactive PowerShell GUI to Collect System, Network, and User Information from Remote Workstations. |
| 4 | +
|
| 5 | +.DESCRIPTION |
| 6 | + This script uses CIM (or fallback to WMI) to collect OS, BIOS, user, and network details |
| 7 | + from a list of workstations input by the user. It features GUI interaction, console hiding, |
| 8 | + structured logging, CSV export, and a responsive progress bar for user feedback. |
| 9 | +
|
| 10 | +.AUTHOR |
| 11 | + Luiz Hamilton Silva - @brazilianscriptguy |
| 12 | +
|
| 13 | +.VERSION |
| 14 | + Last Updated: September 30, 2025 |
| 15 | +#> |
| 16 | + |
| 17 | +# Hide PowerShell console |
| 18 | +if (-not ("Window" -as [type])) { |
| 19 | + Add-Type @" |
| 20 | + using System; |
| 21 | + using System.Runtime.InteropServices; |
| 22 | + public class Window { |
| 23 | + [DllImport("kernel32.dll", SetLastError = true)] |
| 24 | + static extern IntPtr GetConsoleWindow(); |
| 25 | + [DllImport("user32.dll", SetLastError = true)] |
| 26 | + [return: MarshalAs(UnmanagedType.Bool)] |
| 27 | + static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); |
| 28 | + public static void Hide() { |
| 29 | + var handle = GetConsoleWindow(); |
| 30 | + ShowWindow(handle, 0); // SW_HIDE |
| 31 | + } |
| 32 | + } |
| 33 | +"@ |
| 34 | +} |
| 35 | +[Window]::Hide() |
| 36 | + |
| 37 | +# Required GUI assembly |
| 38 | +Add-Type -AssemblyName System.Windows.Forms |
| 39 | + |
| 40 | +# Define paths |
| 41 | +$scriptName = "WorkstationInventory" |
| 42 | +$logDir = "C:\Logs-TEMP" |
| 43 | +$logFile = Join-Path $logDir "$scriptName.log" |
| 44 | +$outputCsv = "C:\AD_Workstation_Inventory.csv" |
| 45 | + |
| 46 | +if (-not (Test-Path $logDir)) { |
| 47 | + New-Item -ItemType Directory -Path $logDir -Force | Out-Null |
| 48 | +} |
| 49 | + |
| 50 | +function Log-Message { |
| 51 | + param( |
| 52 | + [string]$Message, |
| 53 | + [ValidateSet("INFO", "WARNING", "ERROR")] [string]$Type = "INFO" |
| 54 | + ) |
| 55 | + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" |
| 56 | + $entry = "[$timestamp] [$Type] $Message" |
| 57 | + Add-Content -Path $logFile -Value $entry |
| 58 | +} |
| 59 | + |
| 60 | +function Show-InfoMessage { |
| 61 | + param([string]$Message) |
| 62 | + [System.Windows.Forms.MessageBox]::Show($Message, "Information", 'OK', 'Information') | Out-Null |
| 63 | + Log-Message -Message $Message -Type "INFO" |
| 64 | +} |
| 65 | + |
| 66 | +function Show-ErrorMessage { |
| 67 | + param([string]$Message) |
| 68 | + [System.Windows.Forms.MessageBox]::Show($Message, "Error", 'OK', 'Error') | Out-Null |
| 69 | + Log-Message -Message $Message -Type "ERROR" |
| 70 | +} |
| 71 | + |
| 72 | +function Get-ComputerListFromText ($inputText) { |
| 73 | + return $inputText -split "[\r\n]+" | Where-Object { $_ -and $_.Trim() -ne "" } |
| 74 | +} |
| 75 | + |
| 76 | +function Collect-Inventory { |
| 77 | + param ( |
| 78 | + [string[]]$Computers, |
| 79 | + [System.Windows.Forms.ProgressBar]$ProgressBar |
| 80 | + ) |
| 81 | + $Results = @() |
| 82 | + $count = 0 |
| 83 | + $total = $Computers.Count |
| 84 | + |
| 85 | + foreach ($Computer in $Computers) { |
| 86 | + $count++ |
| 87 | + $percent = [math]::Round(($count / $total) * 100) |
| 88 | + $ProgressBar.Value = [math]::Min($percent, 100) |
| 89 | + |
| 90 | + try { |
| 91 | + Log-Message -Message "Querying $Computer..." |
| 92 | + if (Test-Connection -ComputerName $Computer -Count 2 -Quiet) { |
| 93 | + try { |
| 94 | + $OS = Get-CimInstance -Class Win32_OperatingSystem -ComputerName $Computer -ErrorAction Stop |
| 95 | + $BIOS = Get-CimInstance -Class Win32_BIOS -ComputerName $Computer -ErrorAction Stop |
| 96 | + $Net = Get-CimInstance -Class Win32_NetworkAdapterConfiguration -ComputerName $Computer -ErrorAction Stop | Where-Object { $_.IPEnabled -eq $true } |
| 97 | + $User = Get-CimInstance -Class Win32_ComputerSystem -ComputerName $Computer -ErrorAction Stop |
| 98 | + } catch { |
| 99 | + Log-Message -Message "Falling back to WMI for $Computer..." -Type "WARNING" |
| 100 | + $OS = Get-WmiObject -Class Win32_OperatingSystem -ComputerName $Computer |
| 101 | + $BIOS = Get-WmiObject -Class Win32_BIOS -ComputerName $Computer |
| 102 | + $Net = Get-WmiObject -Class Win32_NetworkAdapterConfiguration -ComputerName $Computer | Where-Object { $_.IPEnabled -eq $true } |
| 103 | + $User = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $Computer |
| 104 | + } |
| 105 | + |
| 106 | + $Results += [PSCustomObject]@{ |
| 107 | + Hostname = $Computer |
| 108 | + LoggedUser = $User.UserName |
| 109 | + OperatingSystem = $OS.Caption |
| 110 | + OSVersion = $OS.Version |
| 111 | + LastBootTime = ([Management.ManagementDateTimeConverter]::ToDateTime($OS.LastBootUpTime)) |
| 112 | + IPAddress = $Net.IPAddress -join ', ' |
| 113 | + MACAddress = $Net.MACAddress |
| 114 | + DefaultGateway = $Net.DefaultIPGateway -join ', ' |
| 115 | + AdapterName = $Net.Description |
| 116 | + Domain = $User.Domain |
| 117 | + BIOSManufacturer = $BIOS.Manufacturer |
| 118 | + SerialNumber = $BIOS.SerialNumber |
| 119 | + } |
| 120 | + } else { |
| 121 | + Log-Message -Message "$Computer is unreachable." -Type "WARNING" |
| 122 | + $Results += [PSCustomObject]@{ |
| 123 | + Hostname = $Computer |
| 124 | + LoggedUser = "Unavailable" |
| 125 | + OperatingSystem = "Unreachable" |
| 126 | + OSVersion = "N/A" |
| 127 | + LastBootTime = "N/A" |
| 128 | + IPAddress = "N/A" |
| 129 | + MACAddress = "N/A" |
| 130 | + DefaultGateway = "N/A" |
| 131 | + AdapterName = "N/A" |
| 132 | + Domain = "N/A" |
| 133 | + BIOSManufacturer = "N/A" |
| 134 | + SerialNumber = "N/A" |
| 135 | + } |
| 136 | + } |
| 137 | + } catch { |
| 138 | + Log-Message -Message ("Error querying ${Computer}: $($_.Exception.Message)") -Type "ERROR" |
| 139 | + } |
| 140 | + } |
| 141 | + |
| 142 | + try { |
| 143 | + $Results | Export-Csv -Path $outputCsv -NoTypeInformation -Encoding UTF8 |
| 144 | + Show-InfoMessage -Message "Report generated successfully.\nSaved to: $outputCsv" |
| 145 | + } catch { |
| 146 | + Show-ErrorMessage -Message "Error exporting to CSV: $($_.Exception.Message)" |
| 147 | + } |
| 148 | + |
| 149 | + $ProgressBar.Value = 100 |
| 150 | +} |
| 151 | + |
| 152 | +# GUI - Input for hostnames |
| 153 | +$form = New-Object System.Windows.Forms.Form |
| 154 | +$form.Text = "Workstation Inventory Collector" |
| 155 | +$form.Size = New-Object System.Drawing.Size(500, 420) |
| 156 | +$form.StartPosition = "CenterScreen" |
| 157 | + |
| 158 | +$label = New-Object System.Windows.Forms.Label |
| 159 | +$label.Text = "Enter hostnames or IPs (one per line):" |
| 160 | +$label.Location = '10,10' |
| 161 | +$label.AutoSize = $true |
| 162 | +$form.Controls.Add($label) |
| 163 | + |
| 164 | +$textBox = New-Object System.Windows.Forms.TextBox |
| 165 | +$textBox.Multiline = $true |
| 166 | +$textBox.ScrollBars = 'Vertical' |
| 167 | +$textBox.Location = '10,40' |
| 168 | +$textBox.Size = '460,220' |
| 169 | +$form.Controls.Add($textBox) |
| 170 | + |
| 171 | +$progressBar = New-Object System.Windows.Forms.ProgressBar |
| 172 | +$progressBar.Location = '10,270' |
| 173 | +$progressBar.Size = '460,20' |
| 174 | +$progressBar.Style = 'Continuous' |
| 175 | +$form.Controls.Add($progressBar) |
| 176 | + |
| 177 | +$buttonRun = New-Object System.Windows.Forms.Button |
| 178 | +$buttonRun.Text = "Run Inventory" |
| 179 | +$buttonRun.Location = '10,310' |
| 180 | +$buttonRun.Size = '220,40' |
| 181 | +$buttonRun.Add_Click({ |
| 182 | + $computers = Get-ComputerListFromText -inputText $textBox.Text |
| 183 | + if ($computers.Count -gt 0) { |
| 184 | + $progressBar.Value = 0 |
| 185 | + Collect-Inventory -Computers $computers -ProgressBar $progressBar |
| 186 | + } else { |
| 187 | + Show-ErrorMessage -Message "Please provide at least one valid hostname or IP." |
| 188 | + } |
| 189 | +}) |
| 190 | +$form.Controls.Add($buttonRun) |
| 191 | + |
| 192 | +$buttonClose = New-Object System.Windows.Forms.Button |
| 193 | +$buttonClose.Text = "Close" |
| 194 | +$buttonClose.Location = '250,310' |
| 195 | +$buttonClose.Size = '220,40' |
| 196 | +$buttonClose.Add_Click({ $form.Close() }) |
| 197 | +$form.Controls.Add($buttonClose) |
| 198 | + |
| 199 | +$form.Topmost = $true |
| 200 | +$form.Add_Shown({ $form.Activate() }) |
| 201 | +[void]$form.ShowDialog() |
0 commit comments