Finding machines with from list of serial numbers

by rambog at 2012-09-28 11:53:38

I have a list of serial numbers and need to discover the computer name(pulled from Active Directory) that the serial number belongs to. I will also need to eventually get the DN (distinguished name) where the computer resides in active directory.

The problem I am stuck right outta the gate the the line beginning with $objSearch.SearchRoot. The error is below. I have looked but cannot see the problem. Any help on the syntax and what I am trying to eventually accomplish would be appreciated.

$WOWSerials = Get-Content "C:\LabPC\ComputerReports\WOW\WOWList.txt
$NumSerials= $WowSerials.count
Write-host "number in text file=$NumSerials"
$ObjFilter = "(objectClass=Computer)"
$objSearch = New-Object System.DirectoryServices.DirectorySearcher
$objSearch.PageSize = 20000
$objSearch.Filter = $ObjFilter
$objSearch.SearchRoot = "LDAP://OU=Workstations,DC=lab,DC=test-lab,DC=org"
$AllObj = $objSearch.FindAll()
foreach ($Obj in $AllObj)
{
$objItemT = $Obj.Properties
$CName = $objItemT.name
for ($i=0;$i<$NumSerials;$i++)
{
get-wmiobject -class Win32_BIOS -Computer $CName | Where-Object {$.SerialNumber -matches $WOWSerials}|Out-Host $CName
}
}


And the error,
The string starting:
At C:\scripts\WOWDiscover.ps1:8 char:83
+ $objSearch.SearchRoot = "LDAP://OU=Workstations,DC=lab,DC=test-lab,D
C=org <<<< "
is missing the terminator: ".
At C:\scripts\WOWDiscover.ps1:18 char:7
+ } <<<<
+ CategoryInfo : ParserError: (
$AllObj = $ob…e
}
}:String) [], ParseException
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
by poshoholic at 2012-09-28 12:17:27
That’s a parsing error. You’re missing a closing double-quote on the end of your first line.
by rambog at 2012-09-28 13:52:12
Ouch! I was too focused on line 8. Thank you.

There still is an issue with the code and it rests in the line,

get-wmiobject -class Win32_BIOS -Computer $CName | Where-Object {$
.SerialNumber -like "$WOWSerials"}|Out-Host $CName

It generates the following output/error:
Out-Host : A positional parameter cannot be found that accepts argument ‘System
.DirectoryServices.ResultPropertyValueCollection’.
At C:\scripts\WOWDiscover.ps1:16 char:117
+ get-wmiobject -class Win32_BIOS -Computer $CName | Where-Object {
$.SerialNumber -like "$WOWSerials"}|Out-Host <<<< $CName
+ CategoryInfo : InvalidArgument: (:slight_smile: [Out-Host], ParameterBindin
gException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell
.Commands.OutHostCommand

Note: I have also corrected the -gt to reflect -lt in the for loop. Any ideas on how to re-code that line? I have tried using -like and -eq.
by poshoholic at 2012-09-28 14:52:03
This can be fixed, and I’ll show you how in a moment. You need to rethink your strategy though.

You have a list of serial numbers. You want to find the machine names that match those serial numbers. Your current logic could be described like this:

Look up all computers in AD. For each computer, look at each serial number I have, and for each of those make a remote call to the computer to see if it matches the serial number.

Let’s put that another way. You have X computers. You have Y serial numbers (hopefully Y = X, but not necessarily). For each X, and for each Y (i.e. for X*Y), make a remote network call to get the serial number to see if it matches. In computer science, that’s an O(n^2) operation. If you have 1000 computers and 1000 serial numbers, that’s 1000000 network calls to get the serial number from the BIOS. Do you see what I’m getting at?

Now compare that to this approach:
Look up all computers in AD. For each computer, make a single remote WMI call to get the serial number for that computer. If you have 1000 computers, that’s only 1000 network calls to get the serial number from the BIOS. Plus, you don’t even need your serial number text file in this case.

Here’s what that might look like:
$serialNumberTable = @{} # Here's a hash table we'll create that allows us to find a computer by its serial number
$ObjFilter = "(objectClass=Computer)"
$objSearch = New-Object System.DirectoryServices.DirectorySearcher
$objSearch.PageSize = 20000
$objSearch.Filter = $ObjFilter
$objSearch.SearchRoot = "LDAP://OU=Workstations,DC=lab,DC=test-lab,DC=org"
$AllObj = $objSearch.FindAll()
foreach ($Obj in $AllObj) {
$objItemT = $Obj.Properties
$CName = $objItemT.name
if ($serialNumber = Get-WmiObject -Class Win32_BIOS -ComputerName $CName | Select-Object -ExpandProperty SerialNumber -ErrorAction SilentlyContinue) {
$serialNumberTable[$serialNumber] = $CName
}
}
# Now down here you could look up the serial numbers in your text file, see if they are in the hash table, like this:
$WOWSerials = Get-Content "C:\LabPC\ComputerReports\WOW\WOWList.txt
foreach ($value in $WOWSerials) {
if ($serialNumberTable.Keys -contains $value) {
# Do something important because we have a match
"Computer: $($serialNumberTable[$key]); Serial: $value)"
} else {
Write-Warning "Serial number $value was not found!"
}
}

Note that this is untested, just a rearranged version of your script, but it should get you a little further along.

Oh, and just for completeness, the line of code you were having an issue with is using Out-Host incorrectly. What you really wanted to do there was this:
if (Get-WmiObject -Class Win32_BIOS -ComputerName $CName | Where-Object {$
.SerialNumber -eq $WOWSerials[$i]}) {
$CName
}
by rambog at 2012-09-29 09:32:40
You rock! Thank you very much for the lesson on efficiency in programming. You were extremely helpful.
by poshoholic at 2012-09-30 09:01:36
I’m glad I could help. Since it seems like your question is answered here, I’m going to go ahead and mark this issue as solved. You can always come back and mark it as unsolved later if you still have an issue.
by edtjbre at 2012-10-17 07:02:29
I was looking for something exactlly like this!

but i cant make out the final piece of code…

/J