param([Parameter(Mandatory=$true)] $ComputerFile) # This is a psexec wrapper that can easily be adapted to other needs. # It is made to retrieve the computer hardware model string as reported # by the system BIOS. It requires psexec.exe to work and that WMI can # be queried locally. The computer must also respond to ICMP echo (ping). $startTime = Get-Date # Check that we have the necessary files in the current directory if ( ! ((Test-Path 'psexec.exe') -and (Test-Path 'get-model.vbs'))) { 'You need psexec.exe and get-model.vbs in the current working directory.' 'See www.svendsentech.no for more information' 'Exiting.' exit } # Output files, didn't bother making them parameters... $modelsFile = 'computername-and-model-psexec.txt' $modelsCountFile = 'computer-models-count-psexec.txt' # Prompt before overwriting files. if ( (Test-Path $modelsFile) -or (Test-Path $modelsCountFile) ) { "Output file(s) '$modelsFile' or '$modelsCountFile' exist." $private:answer = Read-Host 'Overwrite? (Y/n) [yes]' if ($private:answer -match '^n') { 'Aborting.'; exit 0 } } function Trim-Model { param([Parameter(Mandatory=$true)][string] $private:model) $private:model = $private:model -replace '\s*\([^)]+\)\s*', '' $private:model = $private:model -replace '\s+$', '' return $private:model } function Get-Model { param([string] $private:computer) if ( ! (Test-Path "\\$private:computer\c$") ) { return 'ERROR: Cannot access remote C:' } # Copy the file to the root of C: (let's just assume C: is writeable) $private:destination = '\\' + $private:computer + '\c$' # Try to copy, check $? for failure (false) Copy-Item '.\get-model.vbs' -Destination $private:destination if ( ! $? ) { return 'ERROR: Could not copy get-model.vbs to remote C:' } # Run the psexec command $private:output = .\psexec.exe \\$private:computer cscript //nologo c:\get-model.vbs . 2> temp-get-models-psexec.tmp if ($private:output -match '### Model: (.+)') { return Trim-Model $matches[1] } # If we get here something failed with psexec or the VBScript return 'ERROR: Unexpected output from get-model.vbs' } # Read in computer names, or IP addresses, from $computerFile and store in the $computers array $computers = Get-Content $computerFile 'Found ' + $computers.Count + ' computers.' 'Processing computers with psexec.exe.' 'This might take a good long while depending on the amount of computers and their availability.' # Initialize hash and populate it $models = @{} $computers | Foreach { "Processing $_..."; if (Test-Connection -Quiet -Count 1 $_) { $models.$_ = Get-Model $_ } else { $models.$_ = 'ERROR: No ping reply' } } # Dump data to file $models.GetEnumerator() | Sort-Object -property @{Expression='Name';Descending=$false},@{Expression='Value';Descending=$false} | Format-Table -AutoSize | Out-File $modelsFile # Count the number of each model, skip error lines $modelsCount = @{} $models.Keys | Foreach { if ($models.$_ -notmatch '^ERROR:') { $modelsCount.$($models.$_) += 1 } } 'Found ' + $modelsCount.Count + ' unique models.' # Dump data to file $modelsCount.GetEnumerator() | Sort-Object -property @{Expression='Value';Descending=$true},@{Expression='Name';Descending=$false} | Format-Table -AutoSize | Out-File $modelsCountFile @" Start time: $startTime End time: $(Get-Date) Input file: $computerFile Output files: $modelsFile, $modelsCountFile "@