Here is my $1.02 solution designed mainly for enterprise sysadmins with hundreds of computers to manage; you can steal the core logic and tailor for non-domain windows systems. Also pulls in the elusive Dell Monitor SN's and manufacture date for warranty purposes.
# Pulls the computer and monitor serial numbers from the local machine
$ComputerNames = $env:COMPUTERNAME
# Alternately specify multiple computer names as follows
# $ComputerNames = "TargetComputer1", "TargetComputer2", "TargetComputer3"
Function Get-ModelSerialNFO { #version 1.04
#Asseses Target computer or computers passed in as a parameter, collects information about Model and Serial Numbers, returns $ComputerMdlSerResults
[cmdletbinding()]
Param (
[Parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerNames = $env:COMPUTERNAME
)
Begin
{
####### GET YOUR LOCAL DOMAIN NFO #########
$DomainName = $env:USERDOMAIN
$Root = [ADSI]"LDAP://RootDSE"
Try { $DomainRoot = ($Root.Get("rootDomainNamingContext")).ToUpper() }
Catch { Write-Warning "Unable to contact Active Directory because $($Error[0]); aborting script!"; Start-Sleep -Seconds 5; Exit }
$DomainDN = "DC=$DomainName,$DomainRoot"
########## END: GET YOUR LOCAL DOMAIN NFO #########
########### GET YOUR LOCAL DOMAIN CONTROLER THAT LOGGED YOU ON ###########
# $logonServer is used as -Server to the Get-ADComputer below, not sure that this is necessary(?)
$logonServer = nltest /dsgetdc: /force
$logonServer = $logonServer.item(0)
$logonServer = $logonServer.TrimStart("DC: \\")
######### END: GET YOUR LOCAL DOMAIN CONTROLER THAT LOGGED YOU ON ###########
######### GET YOUR LOCAL SITE NAME ############
$sAMAccountName = "$env:ComputerName`$"
$searcher = [adsisearcher]"(&(objectClass=Computer)(sAMAccountName=$sAMAccountName))"
$searcher.SearchRoot = "LDAP://$($DomainDN)"
$searcher.SearchScope = 'Subtree'
$searcher.PropertiesToLoad.Add('CanonicalName') | Out-Null
########### END: GET YOUR LOCAL SITE NAME ############
########## SET YOUR LOCAL baseDN PATH ############
$baseDN = "$DomainDN" #ALTERNATELY SPECIFY YOUR OWN baseDN path in AD
########### END: SET YOUR LOCAL baseDN PATH ###########
FUNCTION Verify-ComputerName { #version 1.3
#Verifies that you are connecting to the host you think you are; helps to identify DNS issues.
[cmdletbinding()]
Param (
[Parameter(Mandatory=$false,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true,
HelpMessage="Enter one or more computer names, separated by commas, or passed in through the pipeline.")]
[array]$ComputerNames = $env:COMPUTERNAME,
[Parameter(Mandatory=$false,
HelpMessage="Select this option to get verbose output, as the function runs.")]
[switch]$INTERACTIVE
)
BEGIN
{
FUNCTION ConvertFrom-NSlookup {
[CmdletBinding()]
Param(
[Parameter(Mandatory,Position=0)]
[array]$NSlookupReply
)
$Hash = [ordered]@{}
[int]$AddressCount = 0
foreach($line in $NSlookupReply){
if ($line -notmatch "======================================================================="){
switch -Regex ($line){
'\s*Server\s*:\s+(?<Server>.*$)'{$Hash.Server = $Matches.Server;Break}
'\s*Address\s*:\s+(?<Address>.*$)'{
IF(($Hash.Server -ne $null) -and ($AddressCount -eq 0))
{
$Hash.SVR_Address = $Matches.Address; $AddressCount++; Break
}
ELSEIF(($Hash.Server -ne $null) -and ($AddressCount -eq 1))
{
$Hash.HOST_Address = $Matches.Address; Break
}
}
'\s*Name\s*:\s+(?<Name>.*$)' {$Hash.HostName = $Matches.Name;Break}
default {Break}
}
}
}
RETURN $Hash;
} #END FUNCTION ConvertFrom-NSlookup
FUNCTION CreatePSObjectForVerifyComputerName {
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -value $null
$obj | Add-Member -MemberType NoteProperty -Name "PINGTEST" -value $null
$obj | Add-Member -MemberType NoteProperty -Name "NSLOOKUP" -value $null
$obj | Add-Member -MemberType NoteProperty -Name "RemoteHostNameViaPSEXEC" -value $null
$obj | Add-Member -MemberType NoteProperty -Name "RemoteHostNameViaInvkCmd" -value $null
$obj | Add-Member -MemberType NoteProperty -Name "ComputerNameVerified" -value $null
return $obj
}
} #END BEGIN
PROCESS
{
[ARRAY]$VeryifyComputerNameResults = $NULL
FOREACH ($ComputerName in $ComputerNames)
{
$ping = new-object System.Net.NetworkInformation.Ping
$objVerifyComputerName = CreatePSObjectForVerifyComputerName
$objVerifyComputerName.ComputerName = $ComputerName
TRY {
$reply = $ping.send($ComputerName)
IF ($reply.status -eq "Success")
{
$objVerifyComputerName.PINGTEST = "SUCCESS"
IF ($INTERACTIVE) {
Write-Host "SUCCESS: PINGTEST: Able to ping an IP that resolves to $ComputerName" -BackgroundColor Green -ForegroundColor Yellow
} #Write-Log -Path $LogFile -Message "SUCCESS: PINGTEST: Able to ping $ComputerName" -Level Info
#RESET THE FOLLOWING VARIABLES FOR FOLLOWING IF-THEN LOGIC PROCESSES
$NSLookupRemoteHostAddress = $null
$RemoteHostName = $null
$NSLookupRemoteHostAddress = (ConvertFrom-NSlookup (nslookup $ComputerName)).HOST_Address
IF ($NSLookupRemoteHostAddress -ne $null) #If NSLookup returned an IP, execute the "hostname" command using psexec64.exe, against the IP that was resolved in DNS, in order to grab the RemoteHostName, as seen from the remote computer's perspective.
{
IF ($INTERACTIVE) {
$Message = "SUCCESS: NSLOOKUP: Resolved $ComputerName in DNS to: $NSLookupRemoteHostAddress"
Write-Host $Message -BackgroundColor Green -ForegroundColor Yellow
} #Write-Log -Path $LogFile -Message $Message -Level Info
$objVerifyComputerName.NSLOOKUP = $NSLookupRemoteHostAddress
$PSExecScriptBlock = {param ($NSLookupRemoteHostAddress)
$RemoteHostName = (& psexec64.exe \\$NSLookupRemoteHostAddress -s -nobanner -accepteula powershell -executionpolicy bypass -command "hostname" 2>$Junk)
return $RemoteHostName
}
$PSExecJob = Start-Job -ScriptBlock $PSExecScriptBlock -ArgumentList $NSLookupRemoteHostAddress
$PSExecJob | Wait-Job -Timeout 30 -ErrorAction SilentlyContinue
IF($PSExecJob.state -eq "Running")
{
WRITE-HOST "The job timer ran out for psexec64.exe, forcibly stopping and removing the job!" -ForegroundColor yellow -BackgroundColor Red
$PSExecJob | Stop-Job
$PSExecJob | Remove-Job
}
ELSE
{
$RemoteHostName = $PSExecJob | Receive-Job
$PSExecJob | Remove-Job
}
IF ($RemoteHostName -ne $null) #Compare $ComputerHostName against $RemoteHostName
{
IF ($INTERACTIVE) {
$Message = "SUCCESS: RemoteHostName: $RemoteHostName was obtainined on the remote IP $NSLookupRemoteHostAddress using PSEXEC64"
Write-Host $Message -BackgroundColor Green -ForegroundColor Yellow
} #Write-Log -Path $LogFile -Message $Message -Level Info
$objVerifyComputerName.RemoteHostNameViaPSEXEC = $RemoteHostName
}
ELSE
{
IF ($INTERACTIVE) {
$Message = "FAILURE: RemoteHostName: Unable to obtain the hostname on the remote IP $NSLookupRemoteHostAddress using PSEXEC64, attempting Invoke-Command"
Write-Host $Message -BackgroundColor Red -ForegroundColor Yellow
} #Write-Log -Path $LogFile -Message $Message -Level Warn
$objVerifyComputerName.RemoteHostNameViaPSEXEC = "FAILED"
#$Command = {hostname}
#$RemoteHostName = Invoke-Command -ComputerName $ComputerName -ScriptBlock $Command
$JobScriptBlock = {param ($ComputerName)
$CommandScriptBlock = {$result = C:\Windows\System32\hostname.exe; return $result}
$RemoteHostName = Invoke-Command -ComputerName $ComputerName -ScriptBlock $CommandScriptBlock
return $RemoteHostName
}
$Job = Start-Job -ScriptBlock $JobScriptBlock -ArgumentList $ComputerName
$Job | Wait-Job -Timeout 30 -ErrorAction SilentlyContinue
IF($Job.state -eq "Running")
{
WRITE-HOST "The job timer ran out for Invoke-Command, forcibly stopping and removing the job!" -ForegroundColor yellow -BackgroundColor Red
$Job | Stop-Job
$Job | Remove-Job
}
ELSE
{
$RemoteHostName = $Job | Receive-Job
$Job | Remove-Job
}
IF ($RemoteHostName -ne $null) #Attempt to obtain RemoteHostName on NSLookupRemotehostAddress using Invoke-Command
{
IF ($INTERACTIVE) {
$Message = "SUCCESS: RemoteHostName: $RemoteHostName was obtainined on the remote IP $NSLookupRemoteHostAddress using Invoke-Command"
Write-Host $Message -BackgroundColor Green -ForegroundColor Yellow
} #Write-Log -Path $LogFile -Message $Message -Level Info
$objVerifyComputerName.RemoteHostNameViaInvkCmd = $RemoteHostName
}
ELSE
{
IF ($INTERACTIVE) {
$Message = "FAILURE: RemoteHostName: Unable to obtain the hostname on the remote IP $NSLookupRemoteHostAddress using Invoke-Command"
Write-Host $Message -BackgroundColor Red -ForegroundColor Yellow
} #Write-Log -Path $LogFile -Message $Message -Level Warn
$objVerifyComputerName.RemoteHostNameViaInvkCmd = "FAILED"
}
}
}
ELSE
{
IF ($INTERACTIVE) {
$Message = "FAILURE: NSLOOKUP: Unable to resolve $ComputerName in DNS, SKIPPING: RemoteHostName Resolution"
Write-Host $Message -BackgroundColor Red -ForegroundColor Yellow
} #Write-Log -Path $LogFile -Message $Message -Level Warn
$objVerifyComputerName.NSLOOKUP = "FAILED"
}
IF ($RemoteHostName -eq $ComputerName)
{
IF ($INTERACTIVE) {
$Message = "SUCCESS: MATCH: IP in DNS Remotely Resolves to: $RemoteHostName thereby matching: $ComputerName as expected!"
Write-Host $Message -BackgroundColor Green -ForegroundColor White
} #Write-Log -Path $LogFile -Message $Message -Level Info
$objVerifyComputerName.ComputerNameVerified = $True
}
ELSE
{
IF ($INTERACTIVE) {
$Message = "FAILURE: MATCH: IP in DNS Remotely Resolves to: $RemoteHostName thereby NOT matching: $ComputerName as expected!"
Write-Host $Message -BackgroundColor Red -ForegroundColor Yellow
} #Write-Log -Path $LogFile -Message $Message -Level Warn
$objVerifyComputerName.ComputerNameVerified = $False
}
}
elseif ($reply.status -eq "TimedOut")
{
IF ($INTERACTIVE) {
Write-Host "FAILURE: PINGTEST: TimedOut: Unable to ping $ComputerName" -BackgroundColor Yellow -ForegroundColor Red
$Message = "TROUBLESHOOTING: Ensure $ComputerName is online; check machine based firewalls (i.e. HBSS AV/HIPS) and/or LAN/CAN/WAN Firewalls"
Write-Host $Message -BackgroundColor Yellow -ForegroundColor Red
} #Write-Log -Path $LogFile -Message "FAILURE: PINGTEST: TimedOut: Unable to ping $ComputerName" -Level Warn
#Write-Log -Path $LogFile -Message $Message -Level Warn
$objVerifyComputerName.PINGTEST = "TimedOut"
}
elseif ($reply.status -eq "DestinationHostUnreachable")
{
IF ($INTERACTIVE) {
Write-Host "FAILURE: PINGTEST: DestinationHostUnreachable: Unable to ping $ComputerName" -BackgroundColor Yellow -ForegroundColor Red
$Message = "TROUBLESHOOTING: Ensure $ComputerName is online; check machine based firewalls (i.e. HBSS AV/HIPS) and/or LAN/CAN/WAN Firewalls"
Write-Host $Message -BackgroundColor Yellow -ForegroundColor Red
} #Write-Log -Path $LogFile -Message "FAILURE: PINGTEST: DestinationHostUnreachable: Unable to ping $ComputerName" -Level Warn
#Write-Log -Path $LogFile -Message $Message -Level Warn
$objVerifyComputerName.PINGTEST = "DestinationHostUnreachable"
}
} #END TRY
Catch {
IF ($INTERACTIVE) {
Write-Host "FAILURE: PINGTEST: Unable to ping $ComputerName" -BackgroundColor Yellow -ForegroundColor Red
$Message = "TROUBLESHOOTING: Ensure $ComputerName is online; check machine based firewalls (i.e. HBSS AV/HIPS) and/or LAN/CAN/WAN Firewalls"
Write-Host $Message -BackgroundColor Yellow -ForegroundColor Red
} #Write-Log -Path $LogFile -Message "FAILURE: PINGTEST: Unable to ping $ComputerName" -Level Warn
#Write-Log -Path $LogFile -Message $Message -Level Warn
$objVerifyComputerName.PINGTEST = "FAILURE"
$objVerifyComputerName.ComputerNameVerified = $False
#$reply = [ordered]@{}
#$reply.status = "Failure"
} #END CATCH
$VeryifyComputerNameResults += $objVerifyComputerName
} #END FOREACH ($ComputerName in $ComputerNames)
RETURN $VeryifyComputerNameResults
} #END PROCESS
END
{
} #END END
} #END FUNCTION Verify-ComputerName V1.3
#Method in PS6 uses Get-ItemPropertyValue
#i.e. $AGMRegValueValue = Get-ItemPropertyValue -Path $AGMRegKey -Name $AGMRegValue
$GetRegValue_ScriptBlock = { param ($KeyPath, $ValueName)
function Get-RegValue([String] $KeyPath, [String] $ValueName)
{
(Get-ItemProperty -LiteralPath $KeyPath -Name $ValueName).$ValueName
}
Get-RegValue -KeyPath $KeyPath -ValueName $ValueName
} #This function was written for PS4 to allow greater compatibility
$LogonUIRegKey = "Registry::HKEY_LOCAL_MACHINE\Software\Microsoft\windows\currentVersion\Authentication\LogonUI"
$LastLoggedOnDisplayName = "LastLoggedOnDisplayName"
$LastLoggedOnUser = "LastLoggedOnUser"
} #end Begin
Process
{
[ARRAY]$ComputerMdlSerResults = $NULL
[ARRAY]$MonitorMdlSerResults = $NULL
foreach($ComputerName in $ComputerNames)
{
$ManufacturerModel=$NULL; $SerialNumber=$NULL; $Monitors=$NULL
IF((Verify-ComputerName -ComputerNames $ComputerName).ComputerNameVerified)
{
#get info about manufacturer and model
$ManufacturerModel = Get-WmiObject -Class Win32_ComputerSystem -ComputerName $ComputerName | select name, manufacturer, model
$SerialNumber = Get-WmiObject -Class Win32_BIOS -ComputerName $ComputerName | select SerialNumber
$Monitors = gwmi -Namespace root\wmi -ComputerName $ComputerName -Query "SELECT * FROM WmiMonitorID WHERE Active='True'"
$CurrentUser = $null; $Process=$null;$RemoteComputerLockedAndInUse=$null;$LogOffUser=$null;$UpdateLocalBIOS = $null;
$CurrentUser = gwmi -Class win32_computersystem -ComputerName $ComputerName | select -ExpandProperty username
IF($CurrentUser.Length -gt 1)
{
$SamAccountName = ($CurrentUser.Remove(0,5))
$ADUserObj = Get-ADUser -Filter 'SamAccountName -like $SamAccountName' -Properties * -Server $logonServer -SearchBase $baseDN -SearchScope Subtree
$LastUsedBy = $ADUserObj.DisplayName
}
ELSE
{
$LastUsedBy = Invoke-Command -ComputerName $ComputerName -ScriptBlock $GetRegValue_ScriptBlock -ArgumentList $LogonUIRegKey, $LastLoggedOnDisplayName 2>$null
}
IF($Monitors.count -ge 1)
{
foreach ($Monitor in $Monitors)
{
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "AttachedTo:" -Value $ComputerName
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_Manufacturer" -Value $(($Monitor.ManufacturerName | foreach {IF($_ -NE '00'){[char]$_}}) -join "")
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_Model" -Value $(($Monitor.UserFriendlyName | foreach {IF($_ -NE '00'){[char]$_}}) -join "")
IF($(($Monitor.ManufacturerName | foreach {IF($_ -NE '00'){[char]$_}}) -join "") -eq "DEL")
{
$DellMonitorSN = $(($Monitor.SerialNumberID | foreach {IF($_ -NE '00'){[char]$_}}) -join "")
$DellMonitorSNMod = "xx-0"+ $DellMonitorSN.Substring(0,5) + "-xxxxx-" + $DellMonitorSN.Substring(5,3) + "-" + $DellMonitorSN.Substring(8,4)
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_SerialNumber" -Value $DellMonitorSNMod
$Day = $DellMonitorSN.Substring(7,1)
SWITCH ($Day){
{$Day -eq "A"}{$Day = "10"}
{$Day -eq "B"}{$Day = "11"}
{$Day -eq "C"}{$Day = "12"}
{$Day -eq "D"}{$Day = "13"}
{$Day -eq "E"}{$Day = "14"}
{$Day -eq "F"}{$Day = "15"}
{$Day -eq "G"}{$Day = "16"}
{$Day -eq "H"}{$Day = "17"}
{$Day -eq "I"}{$Day = "18"}
{$Day -eq "J"}{$Day = "19"}
{$Day -eq "K"}{$Day = "20"}
{$Day -eq "L"}{$Day = "21"}
{$Day -eq "M"}{$Day = "22"}
{$Day -eq "N"}{$Day = "23"}
{$Day -eq "O"}{$Day = "24"}
{$Day -eq "P"}{$Day = "25"}
{$Day -eq "Q"}{$Day = "26"}
{$Day -eq "R"}{$Day = "27"}
{$Day -eq "S"}{$Day = "28"}
{$Day -eq "T"}{$Day = "29"}
{$Day -eq "U"}{$Day = "30"}
{$Day -eq "V"}{$Day = "31"}
}
$Month = $DellMonitorSN.Substring(6,1)
SWITCH ($Month){
{$Month -eq 1}{$Month = "JAN"}
{$Month -eq 2}{$Month = "FEB"}
{$Month -eq 3}{$Month = "MAR"}
{$Month -eq 4}{$Month = "APR"}
{$Month -eq 5}{$Month = "MAY"}
{$Month -eq 6}{$Month = "JUN"}
{$Month -eq 7}{$Month = "JUL"}
{$Month -eq 8}{$Month = "AUG"}
{$Month -eq 9}{$Month = "SEP"}
{$Month -eq "A"}{$Month = "OCT"}
{$Month -eq "B"}{$Month = "NOV"}
{$Month -eq "C"}{$Month = "DEC"}
}
$DayOfManuf = "$($Day)-$($Month)-$($Monitor.YearOfManufacture)"
}
ELSE
{
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_SerialNumber" -Value $(($Monitor.SerialNumberID | foreach {IF($_ -NE '00'){[char]$_}}) -join "")
$DayOfManuf = "UNKNOWN"
}
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_ProductCodeID" -Value $(($Monitor.ProductCodeID | foreach {IF($_ -NE '00'){[char]$_}}) -join "")
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_WeekOfManuf" -Value $($Monitor.WeekOfManufacture)
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_YearOfManuf" -Value $($Monitor.YearOfManufacture)
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_DayOfManuf" -Value $DayOfManuf
$obj | Add-Member -MemberType NoteProperty -Name "Monitor_LastUsedBy" -Value $LastUsedBy
$MonitorMdlSerResults += $obj
}
}
IF($ManufacturerModel -ne $null -and $SerialNumber -ne $null)
{
$obj = New-Object PSObject
$obj | Add-Member -MemberType NoteProperty -Name "ComputerName" -Value $ManufacturerModel.name
$obj | Add-Member -MemberType NoteProperty -Name "Computer_Manufacturer" -Value $ManufacturerModel.manufacturer
$obj | Add-Member -MemberType NoteProperty -Name "Computer_Model" -Value $ManufacturerModel.model
$obj | Add-Member -MemberType NoteProperty -Name "BIOS_SerialNumber" -Value $SerialNumber.SerialNumber
$obj | Add-Member -MemberType NoteProperty -Name "Computer_LastUsedBy" -Value $LastUsedBy
$ComputerMdlSerResults += $obj
}
}
} #END foreach($ComputerName in $ComputerNames)
return $ComputerMdlSerResults, $MonitorMdlSerResults
} #End Process
End{} #End End
} #End FUNCTION #version 1.04
CLS
$RESULTS = Get-ModelSerialNFO $ComputerNames
#Sets a directory on your workstation to create the CSV log files
[string]$ResultsFilePath = "$env:SystemDrive\LOGS\ModelSerialNFO\" #Set Root Directory for the CSV Output File
########## CREATE USG LOG DIRECTORY, IF NEEDED ##########
IF(!(Test-Path $ResultsFilePath -PathType Container )){
Write-Host "WARNING: RESULTS CONTAINER `"$ResultsFilePath`" DOES NOT EXIST;
ATTEMPTING TO CREATE..." -BackgroundColor Yellow -ForegroundColor Red
TRY {
md $ResultsFilePath -Force -ErrorAction Stop
$ResultsFolderExists = $true
Write-Host "SUCCESS: RESULTS CONTAINER `"$ResultsFilePath`" WAS SUCCESSFULLY CREATED!" -BackgroundColor Blue -ForegroundColor White
}
CATCH
{
$ResultsFolderExists = $false
Write-Host "FAILURE: UNABLE TO CREATE RESULTS CONTAINER, ABORTING ACTION(s)!" -BackgroundColor RED -ForegroundColor YELLOW
}
}ELSEIF($RunningResults2csv){Write-Host "INFO: RESULTS CONTAINER `"$ResultsFilePath`" ALREADY EXISTS; CONTINUING SCRIPT..." -BackgroundColor Blue -ForegroundColor White; $ResultsFolderExists = $true}
######### END: CREATE USG LOG DIRECTORY, IF NEEDED ##########
$outFile = $ResultsFilePath + "$($OU)_COMPUTER_ModelSerialNFO_REPORT_" + (Get-date -Format "dd-MM-yyyy") + '.CSV' #Set the name of the CSV Output File
$outFile2 = $ResultsFilePath + "$($OU)_MONITOR_ModelSerialNFO_REPORT_" + (Get-date -Format "dd-MM-yyyy") + '.CSV' #Set the name of the CSV Output File
$RESULTS[0] | ft -AutoSize #This is your COMPUTER ModelSerial Info
$RESULTS[1] | ft -AutoSize #This is the attached MONITOR ModelSerial Info
$RESULTS[0] | Export-Csv -LiteralPath $outFile -NoTypeInformation #This is your COMPUTER ModelSerial Info - exported to a CSV file in your C:\Logs\ModelSerialNFO directory.
$RESULTS[1] | Export-Csv -LiteralPath $outFile2 -NoTypeInformation #This is the attached MONITOR ModelSerial Info - exported to a CSV file in your C:\Logs\ModelSerialNFO directory.