Monitor Serial Numbers of Remote Machines from Text File

Hi All,
Sorry, though completely new to powershell and trying to learn for the first time this week but really lost and have very short deadline to obtain,so hoping someone can help so i dont have to manually walk and scan each monitor.

I want to scan a list of machines (text file I) that I got from a DSquery and display their Make/Model/Serial number along side of the Hostname of the machine, and the 2 last logged in users of the machine.
Any help or advice would be greatly appreciated. I need in similar format as the most i would have is 4 monitors connected and will then use this data to input into other systems. Thanks

PS C:\Scripts> Get-Monitor -ComputerName $Computers

PC Name / PC Model / PC Serial Number / L.Logged In User 1 / L.Logged in User 2 /Monitor Make Model/ Monitor SN /


PC1SYD1234 DEL 7500 XXXXX mrX, Mrs Y HP HP LA2405x CN12345678 SSL7-F108F-9D4Z
PC1SYD1234 DEL 7500 XXXXX mrX, Mrs Y HP HP LA2405x CN12345679 SSL7-F108F-9D4Z
PC1SYD1234 DEL 7500 XXXXX mrX, Mrs Y HP HP LA2405x CN123456710 SSL7-F108F-9D4Z

PC2SYD1235 DEL 7500 XXXXX mrX, Mrs Y HP HP LA2405x CN123456710 SSL7-F108F-9D4Z
PC2SYD1235 DEL 7500 XXXXX mrX, Mrs Y HP HP LA2405x CN123456710 SSL7-F108F-9D4Z

I need to make sure it caters for Dell Monitors that apparently have issues with scripts from what I’ve read, so may need to some how incorporate:
function Decode {
If ($args[0] -is [System.Array]) {
[System.Text.Encoding]::ASCII.GetString($args[0])
}
Else {
“Not Found”
}
}

echo “Name, Serial”

ForEach ($Monitor in Get-WmiObject WmiMonitorID -Namespace root\wmi) {
$Name = Decode $Monitor.UserFriendlyName -notmatch 0
$Serial = Decode $Monitor.SerialNumberID -notmatch 0

echo "$Name, $Serial"

}

Not sure how much you already have written, but since you mention using a “get-monitor” command I’m assuming that’s a function you’ve started writing. If not, here is what you might use to start.

function Get-Monitor {
    Param(
        [String[]]$Computername
    )
    Foreach ($Computer in $Computername){

    }
}

You can pass your list of computers to that function, which will then loop through each one of them and do stuff. In this case it sounds like you just need to do a few WMI (or CIM) queries. So maybe something like this:

function Get-Monitor {
    Param(
        [String[]]$Computername
    )
    Foreach ($Computer in $Computername){
        $ComputerSystemInfo = Get-WmiObject -Class win32_computersystem -ComputerName $computer
        $MonitorInfo = Get-WmiObject -Namespace root\wmi -Class WmiMonitorID -ComputerName $computer
        $BIOSInfo = Get-WmiObject -Class win32_bios -ComputerName $computer
    }
}

That gets all the info into memory, probably more than you are actually looking for. The last thing to do then is to just create an output object to cherry pick the specific items you want in your output.

function Get-Monitor {
    Param(
        [String[]]$Computername
    )
    Foreach ($Computer in $Computername){
        $ComputerSystemInfo = Get-WmiObject -Class win32_computersystem -ComputerName $computer
        $MonitorInfo = Get-WmiObject -Namespace root\wmi -Class WmiMonitorID -ComputerName $computer
        $BIOSInfo = Get-WmiObject -Class win32_bios -ComputerName $computer

        $output = New-Object PSObject -Property @{
            'Name' = $ComputerSystemInfo.Name
            'Model' = $ComputerSystemInfo.Model
            'SerialNumber' = $BIOSInfo.SerialNumber
            'LoggedOnUser' = $ComputerSystemInfo.UserName
        }
    }
}

I left out the monitor stuff since that’s not a strict one to one mapping. You could have one or more monitors, so I’d probably use add-member to append those.

function Get-Monitor {
    Param(
        [String[]]$Computername
    )
    Foreach ($Computer in $Computername){
        $ComputerSystemInfo = Get-WmiObject -Class win32_computersystem -ComputerName $computer
        $MonitorInfo = Get-WmiObject -Namespace root\wmi -Class WmiMonitorID -ComputerName $computer
        $BIOSInfo = Get-WmiObject -Class win32_bios -ComputerName $computer

        $output = New-Object PSObject -Property @{
            'Name' = $ComputerSystemInfo.Name
            'Model' = $ComputerSystemInfo.Model
            'SerialNumber' = $BIOSInfo.SerialNumber
            'LoggedOnUser' = $ComputerSystemInfo.UserName
        }
        for ($i = 0; $i -lt $MonitorInfo.Length; $i++){ 
            $output | add-member -MemberType NoteProperty -Name "Monitor$i`Name" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].ManufacturerName)))
            $output | Add-Member -MemberType NoteProperty -name "Monitor$i`Serial#" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].SerialNumberid)))
        }
        $output
    }
}

Not to dampen the project but keep in mind when getting serial numbers from the monitors, the values ‘burned’ in the monitor itself can be different than what the outside or packaging shows; a lot of times it’s a shortened version of the full serial number.

The internal value depends on how the manufacturer has entered it, and unfortunately with many of them, they don’t always match (based on my experience). However, the script can still be useful as some will work.

Thanks guys,
Much appreciated for the prompt response.
Unfortunately I hadn’t written much so far as powershell is virtually brand new to me,
I had played around with vbscript many years ago and read several forums warning of especially Dell using different Serial numbers than what is on the sticker and apparently the need to search for “Friendly Names/Numbers”.
I’ll have a go at the above when i get to work.
Enjoy your Day/Weekend and Thanks again.

Sorry if this sounds silly, but when looking at this in Powershell ISE, I am getting errors,
One of them being "Missing Expression after =… Not too sure why as I’m pretty sure I have used other scripts with similar format.
SideQuestion, anyone in Epping Area Sydney that would be interested in some Tutoring lessons on PowerShell, paid of course?
Just so I can get the basics downpacked and not have to disturb others so often.

function Get-Monitor {
Param(
[String]$Computernames = get-content “c:\scripts\machinelist.txt”
)
Foreach ($Computer in $Computernames){
$ComputerSystemInfo = Get-WmiObject -Class win32_computersystem -ComputerNames $computer
$MonitorInfo = Get-WmiObject -Namespace root\wmi -Class WmiMonitorID -ComputerNames $computer
$BIOSInfo = Get-WmiObject -Class win32_bios -ComputerNames $computer

    $output = New-Object PSObject -Property @{
        'Name' = $ComputerSystemInfo.Name
        'Model' = $ComputerSystemInfo.Model
        'SerialNumber' = $BIOSInfo.SerialNumber
        'LoggedOnUser' = $ComputerSystemInfo.UserName
    }
    for ($i = 0; $i -lt $MonitorInfo.Length; $i++){
        $output | add-member -MemberType NoteProperty -Name "Monitor$i`Name" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].ManufacturerName)))
        $output | Add-Member -MemberType NoteProperty -name "Monitor$i`Serial#" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].SerialNumberid)))
    }
    $output
}

}

Update, Overcome the first error, but now getting a heap of others and only partial results:
Any ideas?

function Get-Monitor {}
Param(
[String]$Computernames = get-content “c:\scripts\machinelist.txt”
)
Foreach ($Computer in $Computernames){
$ComputerSystemInfo = Get-WmiObject -Class win32_computersystem -ComputerNames $computer
$MonitorInfo = Get-WmiObject -Namespace root\wmi -Class WmiMonitorID -ComputerNames $computer
$BIOSInfo = Get-WmiObject -Class win32_bios -ComputerNames $computer

    $output = New-Object PSObject -Property @{
        'Name' = $ComputerSystemInfo.Name
        'Model' = $ComputerSystemInfo.Model
        'SerialNumber' = $BIOSInfo.SerialNumber
        'LoggedOnUser' = $ComputerSystemInfo.UserName
    }
    for ($i = 0; $i -lt $MonitorInfo.Length; $i++){
        $output | add-member -MemberType NoteProperty -Name "Monitor$i`Name" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].ManufacturerName)))
        $output | Add-Member -MemberType NoteProperty -name "Monitor$i`Serial#" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].SerialNumberid)))
    }
    $output
}

Sorry for the floods, but I think I have almost figured it out, though I get an error about PARAM not being correct,
however when I ctrl f and search for param, nothing found,
function Get-Monitor {}
Param(
[String]$Computernames = get-content “c:\scripts\machinelist.txt”
)
Foreach ($Computer in $Computernames){
$ComputerSystemInfo = Get-WmiObject -Class win32_computersystem -Computer $Computer
$MonitorInfo = Get-WmiObject -Namespace root\wmi -Class WmiMonitorID -Computer $Computer
$BIOSInfo = Get-WmiObject -Class win32_bios -Computer $Computer

    $output = New-Object PSObject -Property @{
        'Name' = $ComputerSystemInfo.Name
        'Model' = $ComputerSystemInfo.Model
        'SerialNumber' = $BIOSInfo.SerialNumber
        'LoggedOnUser' = $ComputerSystemInfo.UserName
    }
    for ($i = 0; $i -lt $MonitorInfo.Length; $i++){
        $output | add-member -MemberType NoteProperty -Name "Monitor$i`Name" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].ManufacturerName)))
        $output | Add-Member -MemberType NoteProperty -name "Monitor$i`Serial#" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].SerialNumberid)))
    }
    $output
}

-ComputerName not -Computer, the error message will show you the line no with problem.
Get-Help Get-WmiObject -Examples will show you some examples

Thanks mate,
I modified, now getting the below:
In the meantime, ill look up the examples.

PS C:\Scripts> C:\Scripts\MonitorScan.ps1
Param : The term ‘Param’ is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was
included, verify that the path is correct and try again.
At C:\Scripts\MonitorScan.ps1:2 char:5

  • Param(
    
  • ~~~~~
    
    • CategoryInfo : ObjectNotFound: (Param:String) , CommandNotFoundException
    • FullyQualifiedErrorId : CommandNotFoundException

SerialNumber LoggedOnUser Name Model


R89ARPG T410\Admin T410 2537MD4

function Get-Monitor {} should be function Get-Monitor {, the matching block ending } goes at the bottom of file.

I recommend that you watch the Microsoft Virtual Academy (mva.microsoft.com) Powershell videos on getting started and then advanced tools & scripts - that will self enable you to a great extent. Probably the best way to train yourself.

Thanks,
Watching the first video already right now funily enough. thanks again, enjoy your weekend.

Updated Script: just in case anyone else is after a similar script, I think its now working as I am getting results and no error so you can modify the below to your needs. thanks all:

function Get-Monitor {
Param
[String]$Computernames = get-content “c:\scripts\machinelist.txt” }

        Foreach ($Computer in $Computernames){
    $ComputerSystemInfo = Get-WmiObject -Class win32_computersystem -ComputerName $Computer
    $MonitorInfo = Get-WmiObject -Namespace root\wmi -Class WmiMonitorID -ComputerName $Computer
    $BIOSInfo = Get-WmiObject -Class win32_bios -ComputerName $Computer

    $output = New-Object PSObject -Property @{
        'Name' = $ComputerSystemInfo.Name
        'Model' = $ComputerSystemInfo.Model
        'SerialNumber' = $BIOSInfo.SerialNumber
        'LoggedOnUser' = $ComputerSystemInfo.UserName
    }
    for ($i = 0; $i -lt $MonitorInfo.Length; $i++){
        $output | add-member -MemberType NoteProperty -Name "Monitor$i`Name" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].ManufacturerName)))
        $output | Add-Member -MemberType NoteProperty -name "Monitor$i`Serial#" -Value $([system.text.encoding]::ascii.GetString($($MonitorInfo[0].SerialNumberid)))
    }
    $output
}

Hi All,
I have come across a great script that seems to work,
However just need to figure out where/how i can get it to include the PC’s Serial Number:
If anyone interested, here is the Script:
And if anyone is able to spare me some time to help me figure out how to get the PC Serial Number that would be greatly appreciated.
Thanks,

Param(
	[switch]$CSVReport,
	[string]$CSVOutputFile = "C:\Test\AttachedMonitors.csv",
	[switch]$HTMLReport = $true,
	[string]$HTMLOutputFile = "C:\Test\AttachedMonitors.htm",
	[switch]$EmailReportAsHTML,
	[switch]$FromAD,
	[switch]$FromFile,
	[string]$FromFileLocation = "C:\Test\Servers.txt",
	[switch]$Test
)
### SMTP Mail Settings
$SMTPProperties = @{
	To = "ToUser@domain.com"
	From = "FromUser@Domain.com"
	Subject = "Monitor Inventory"
	SMTPServer = "mail.domain.com"
}

$Results = @()
$Count = 0

if ($FromAD)
{	### Attempts to Import ActiveDirectory Module. Produces error if fails.
	Try { Import-Module ActiveDirectory -ErrorAction Stop }
	Catch { Write-Host "Unable to load Active Directory module, is RSAT installed?"; Break }
}

### HTML Header	
$Header = @"

	
		TABLE {border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;margin-left: auto;margin-right: auto;}
		TH {border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color: #808080;}
		TD {border-width: 1px;padding: 3px;border-style: solid;border-color: black;}
		.odd  { background-color:#ffffff; }
		.even { background-color:#dddddd; }
	


	Attached Monitor Inventory Report

"@


Function ConvertTo-Char ($Array)
{
	$Output = ""
	ForEach($char in $Array)
	{	$Output += [char]$char -join ""
	}
	return $Output
}

Function Detect-VirtualMachine
{
	Param (
		[string]$ComputerName
	)
	$VMModels = @("Virtual Machine","VMware Virtual Platform","Xen","VirtualBox")
	$CheckPhysicalOrVMQuery = Get-WmiObject -ComputerName $ComputerName -Query "Select * FROM Win32_ComputerSystem" -Namespace "root\CIMV2" -ErrorAction Stop
	if ($VMModels -contains $CheckPhysicalOrVMQuery.Model)
	{	$IsVM = $True
	}
	Else
	{	$IsVM = $False
	}
	Return $IsVM
}

Function Set-AlternatingRows
{
	<#
	.SYNOPSIS
		Simple function to alternate the row colors in an HTML table
	.DESCRIPTION
		This function accepts pipeline input from ConvertTo-HTML or any
		string with HTML in it.  It will then search for  and replace 
		it with .  With the combination of CSS it
		can set alternating colors on table rows.
		
		CSS requirements:
		.odd  { background-color:#ffffff; }
		.even { background-color:#dddddd; }
		
		Classnames can be anything and are configurable when executing the
		function.  Colors can, of course, be set to your preference.
		
		This function does not add CSS to your report, so you must provide
		the style sheet, typically part of the ConvertTo-HTML cmdlet using
		the -Head parameter.
	.PARAMETER Line
		String containing the HTML line, typically piped in through the
		pipeline.
	.PARAMETER CSSEvenClass
		Define which CSS class is your "even" row and color.
	.PARAMETER CSSOddClass
		Define which CSS class is your "odd" row and color.
	.EXAMPLE $Report | ConvertTo-HTML -Head $Header | Set-AlternateRows -CSSEvenClass even -CSSOddClass odd | Out-File HTMLReport.html
	
		$Header can be defined with a here-string as:
		$Header = @"
		
		TABLE {border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
		TH {border-width: 1px;padding: 3px;border-style: solid;border-color: black;background-color: #6495ED;}
		TD {border-width: 1px;padding: 3px;border-style: solid;border-color: black;}
		.odd  { background-color:#ffffff; }
		.even { background-color:#dddddd; }
		
		"@
		
		This will produce a table with alternating white and grey rows.  Custom CSS
		is defined in the $Header string and included with the table thanks to the -Head
		parameter in ConvertTo-HTML.
	.NOTES
		Author:         Martin Pugh
		Twitter:        @thesurlyadm1n
		Spiceworks:     Martin9700
		Blog:           www.thesurlyadmin.com
		
		Changelog:
			1.0         Initial function release
	.LINK
		http://community.spiceworks.com/scripts/show/1745-set-alternatingrows-function-modify-your-html-table-to-have-alternating-row-colors
	#>
	[CmdletBinding()]
	Param(
		[Parameter(Mandatory=$True,ValueFromPipeline=$True)]
		[string]$Line,
	   
		[Parameter(Mandatory=$True)]
		[string]$CSSEvenClass,
	   
		[Parameter(Mandatory=$True)]
		[string]$CSSOddClass
	)
	Begin {
		$ClassName = $CSSEvenClass
	}
	Process {
		If ($Line.Contains(""))
		{	$Line = $Line.Replace("","")
			If ($ClassName -eq $CSSEvenClass)
			{	$ClassName = $CSSOddClass
			}
			Else
			{	$ClassName = $CSSEvenClass
			}
		}
		Return $Line
	}
}# End Set-AlternatingRows Function

if ($FromAD){ $Computers = Get-ADComputer -Filter * -Properties Name }
if ($FromFile){ $Computers = Get-Content $FromFileLocation }

ForEach ($Computer in $Computers)
{
	$progress = @{
		Activity = "Querying Connected Monitors on $ComputerName"
		Status = "$Count of $($Computers.Count) completed"
		PercentComplete = $Count / $($Computers.Count) * 100
		Id = 0
	}
	Write-Progress @progress
	
	$Count++
	
	if ($FromAD)
	{	$ComputerName = $Computer.DNSHostName
	}
	ElseIf ($FromFile)
	{	$ComputerName = $Computer
	}
	ElseIf ($Test)
	{	Write-Host "Test Mode Enabled" -ForegroundColor Yellow
		$ComputerName = $Env:ComputerName
	}
	Try
	{
		if (-not ($Test))
		{	$IsPhysicalMachine = Detect-VirtualMachine -ComputerName $ComputerName
		}
		Else
		{	$IsPhysicalMachine = Detect-VirtualMachine -ComputerName "localhost"
		}
	}
	Catch
	{
		if (-not ($Test))
		{	Write-Host "ComputerName: $($ComputerName), caught an error checking if the computer was physical or virtual: $($Error[0])" -ForegroundColor Red -BackgroundColor Black
		}
		Else
		{	Write-Host "ComputerName: $($ComputerName), caught an error checking if the computer was physical or virtual: $($Error[0])" -ForegroundColor Red -BackgroundColor Black
		}
		$Results += New-Object PSObject -Property @{
			ComputerName = $ComputerName
			Active = "N/A"
			Manufacturer = "N/A"
			UserFriendlyName = "N/A"
			SerialNumber = "N/A"
			WeekOfManufacture = "N/A"
			YearOfManufacture = "N/A"
			Status = "2 - Warning"
			Message = "There was a problem checking if the computer was physical or virtual: $($Error[0])"
		}
		Continue
	}
	If ($IsPhysicalMachine -eq $false)
	{
		Try
		{
			if (-not ($Test))
			{	$Query = Get-WmiObject -ComputerName $ComputerName -Query "Select * FROM WMIMonitorID" -Namespace root\wmi -ErrorAction Stop
			}
			Else
			{	$Query = Get-WmiObject -Query "Select * FROM WMIMonitorID" -Namespace root\wmi -ErrorAction Stop
			}

			ForEach ($Monitor in $Query)
			{    
				$Results += New-Object PSObject -Property @{
					ComputerName = $ComputerName
					Active = $Monitor.Active
					Manufacturer = ConvertTo-Char($Monitor.ManufacturerName)
					UserFriendlyName = ConvertTo-Char($Monitor.userfriendlyname)
					SerialNumber = ConvertTo-Char($Monitor.serialnumberid)
					WeekOfManufacture = $Monitor.WeekOfManufacture
					YearOfManufacture = $Monitor.YearOfManufacture
					Status = "0 - OK"
					Message = "N/A"
				}
			}
			Continue
		}
		Catch
		{
			$Results += New-Object PSObject -Property @{
				ComputerName = $ComputerName
				Active = "N/A"
				Manufacturer = "N/A"
				UserFriendlyName = "N/A"
				SerialNumber = "N/A"
				WeekOfManufacture = "N/A"
				YearOfManufacture = "N/A"
				Status = "1 - Error"
				Message = "Error: $($Error[0])"
			}
		}
	}
	Else
	{	Write-Host "ComputerName: $($ComputerName) was a virtual machine. Skipping monitor inventory." -ForegroundColor Yellow
		$Results += New-Object PSObject -Property @{
			ComputerName = $ComputerName
			Active = $false
			Manufacturer = "N/A"
			UserFriendlyName = "N/A"
			SerialNumber = "N/A"
			WeekOfManufacture = "N/A"
			YearOfManufacture = "N/A"
			Status = "N/A - Informational"
			Message = "Virtual Machine, skipped monitor inventory."
		}
	}
}

### Debugging to make sure there are results.
#Write-Host "Results Count: $($Results.count)"

If ($Results.count -gt 0)
{	$Results = $Results | Select ComputerName,Active,Manufacturer,UserFriendlyName,SerialNumber,WeekOfManufacture,YearOfManufacture,Status,Message
	if ($CSVReport)
	{	$Results | Sort Status,ComputerName | Export-CSV -Path $CSVOutputFile -NoTypeInformation
	}
	if ($HTMLReport)
	{	$Results | Sort Status,ComputerName | ConvertTo-HTML -Head $Header | Out-File $HTMLOutputFile
	}
	if ($EmailReportAsHTML)
	{	$Body = $Results | Sort Status,ComputerName | ConvertTo-HTML -Head $Header | Out-String
		Send-MailMessage @SMTPProperties -Body $Body -BodyAsHTML
	}
}
Else
{	Write-Output "There were 0 results in the `$Results array."
}

I can’t seem to locate this bit on your script. Well, this is how you get the serial number of a computer. Maybe assign it to a variable and stick to your report, also change the localhost to a computer name variable.

(gwmi Win32_Bios -ComputerName localhost).SerialNumber