Find a computer’s model using PowerShell

Interestingly this blog’s most popular post is one where I demonstrate how to find the serial number of a PC in a batch file so you can write a script that does different things for different computer models.

I pretty much don’t write batch code any more, instead using PowerShell as much as possible. So I thought I’d add a post explaining how to get your computer model in PowerShell as well. If you’ve any experience using PowerShell then you know this could be a very short post…

And the answer?

(Get-WmiObject -Class:Win32_ComputerSystem).Model

Extending that to a specific model to allow us to work on something specific we have many options to play with. My preferred method if I’m only dealing with one model would be to filter at the point of making the query, like so:

Get-WmiObject -Class:Win32_ComputerSystem -Filter:"Model LIKE '%H77ITX%'" -ComputerName:localhost

Notice I’ve filtered the query using the LIKE operator which in WMI queries requires the % character as the wildcard indicator, not *. I’ve also specified the ComputerName as localhost in the above commands. Usually WMI commands operate on network objects, even if you only have the one PC, and as a result can take a little time to respond. If you specify localhost it speeds up the command and ensures you only get a result from the PC where you run your script. Of course if you want to do this on remote PCs then you’d use the ComputerName to specify one or more remote PCs.

Anyway that command is only going to return the object if we are running the query on the right PC model. That’s not too useful on its own so we need to make it return something like True or False, then we can work inside an If statement perhaps. Actually we don’t need to bother! If handles a returned object as if that means True, and no returned object as meaning False.

For example:

If(Get-WmiObject -Class:Win32_ComputerSystem -Filter:"Model LIKE '%H78ITX%'" -ComputerName:localhost)
{ Write-Host "Found an H78ITX model" }
Else
{ Write-Host "Model not found" }

OK so what do we do if we’ve got different models and we want to do different things on different models? Easy, go back to the first command, and use its output with the Switch statement.

$pcModel = (Get-WmiObject -Class:Win32_ComputerSystem).Model
Switch -wildcard ($pcModel)
{
    "*Latitude*" { # install special apps for a Dell }
    "*Elite*"    { # install special apps for an HP }
    default      { # install other things for everything else }
}

There’s plenty you can do with the switch statement, even using regex. There’s a good explanation of it here.

Of course you might want to do things for specific models, and also things for all models from a particular vendor. We can achieve that by being less specific with our initial command and just select different properties of the object from the variable like this.

$computerSystem = (Get-WmiObject -Class:Win32_ComputerSystem)
Write-Host $computerSystem.Manufacturer
Write-Host $computerSystem.Model

Hopefully that shows you how to get the model and manufacturer, then you can construct some logic around both.

Finally, something else to consider is that if you want to drill down using the serial number of a machine we need to make use of a different WMI object, namely Win32_BIOS, like this:

(Get-WmiObject -Class:Win32_BIOS).SerialNumber

A way to run a 64-bit process from a 32-bit script (like add a registry key)

Say you have a need to add a registry key into the 64-bit registry hive but you’re stuck with doing it from a vbscript running in a 32-bit process. For example SCCM always runs vbscript in a 32-bit process, even on a 64-bit Windows machine! If you try this normally Windows 7 64-bit will redirect the key to the virtual 32-bit hive. So running in a 32-bit process if we want to add a key to HKLM\Software\ you will find it will always end up in HKLM\Software\Wow6432Node\ no matter what you do.

I couldn’t find a way to do it directly using vbscript in the short time I spent looking into it, but I did find a sneaky indirect way. My method is to execute a command to add a scheduled task into Windows that will do it for you!

I’ve written a bit of vbscript which will create a run-once self-deleting scheduled task that can be used to run a command such as REG ADD which will be running as a 64-bit process, assuming you use it on Windows x64. The only thing you need to have is admin rights, the rest will happen automatically.

You could use it for all sorts of things, like getting access to an area of the OS that needs LOCAL SYSTEM rights for example. You must remember though it’s just for firing off a command that you don’t need a response from. You need to test whatever you fire-off because you can’t check what you’ve done from within the script!

Click here to see my function on pastebin

To use the vbscript function you need to call the CreateJob() function and pass it the command that you want to run. For example if we call the following…

CreateJob("REG ADD HKLM\Software\64BitKey /v 64BitValue /d 64BitData /f")

…then my function will create a scheduled task that runs the command between the quotes 1 minute after it is created. Once the task completes it will then delete itself automatically, whether it succeeds or fails. The command in my example will create a registry key HKLM\Software\64BitKey with a new REG_SZ value 64BitValue which has the string data 64BitData.

I’ve commented the code as best I can. Basically the scheduled task that is created will have a unique name every time due the use of a guid string for the name. The task will work on XP, Vista and Windows 7, and on 32-bit or 64-bit, but it will always be in ‘XP mode’ so that it will delete itself after it is executed.

Here’s a slightly generic example of use:

Adding a registry key HKLM\Software\RegKey with KeyName that has a DWORD value of 000000FF:
CreateJob("REG ADD HKLM\Software\RegKey /v KeyName /t REG_DWORD /d 0xFF /f")
Notice here the use of 0xFF to specify the hex value, and the /f switch to force the key to add. If we don’t use /f and the value is already there then the command will perpetually wait for a response.

Here are some screenshots proving it works…

1. running the script in an Admin CMD prompt running in 32-bit mode on a 64-bit machine

2. proving the script is running as a 32-bit process

3. showing the scheduled task about to run

4. and finally the registry key after it has been created, definitely in the 64-bit hive!

For more info about the REG ADD command either go here, try typing reg add /? in the command-line, or you could Google it.

The right way to do my precise example is use WMI as mentioned here but my way is more flexible because you can do other stuff like run apps in a 64-bit process as well…

Anyway, here’s my function. Have fun with it, and don’t forget, you’re running as the SYSTEM account when you use this, so please be careful!


Function CreateJob(strCommand)
    Const SHELL_WAIT = True
    Const SHELL_HIDE = 0
    CreateJob = False
    ' Get date & time 1 minute in advance
    ' And it must be at least 1 minute
    ' Source: w3schools, & mikeblas on hardforum.com
    Dim strDateTime : strDateTime = DateAdd("n", 1, Now())
    Dim strDate     : strDate = LEFT(strDateTime, InStr(strDateTime, " ")-1)
    Dim strTime     : strTime = MID(strDateTime, InStr(strDateTime, " ")+1)

    ' define the command we will run to create the once-only scheduled task
    ' uses a new guid for the name each time so it will be a unique task
    Dim strJobCmd   : strJobCmd = "schtasks.exe /Create /TN " & _
        getGuid & " /RU SYSTEM /ST " & _
        strTime & " /SD " & _
        strDate & " /SC ONCE /TR """ & _
        strCommand & """"
        ' on Vista/Win7 must create task as XP-readable type using /V1
        ' this is so it will delete itself propely (bug in schtasks) using /Z
        If onVistaWin7 Then strJobCmd = strJobCmd & " /Z /V1"
    WScript.echo strJobCmd
    Dim oJobShell : Set oJobShell = CreateObject("WScript.Shell")
    Dim jobRet : jobRet = oJobShell.Run(strJobCmd, SHELL_HIDE, SHELL_WAIT)
    If jobRet = 0 Then CreateJob = True
    ' here we tried to make the task and get the result to a variable
    ' if the return is non-zero then the creation of the task errored
    Set oJobShell = Nothing
End Function

Function getGuid
    ' this functions gets a unique guid and returns it as a string
    Dim TypeLib : Set TypeLib = CreateObject("Scriptlet.TypeLib")
    getGuid = Left(CStr(TypeLib.Guid),38)
    ' above line also removes some strageness at the end
    Set TypeLib = Nothing
End Function

Function onVistaWin7
    ' this function returns true on Vista or above (incl. Srv2008)
    Dim colOSver, objOSver
    onVistaWin7 = False
    Set colOSver = GetObject("WinMgmts:root\cimv2").ExecQuery _
        ("Select Version from Win32_OperatingSystem")
    For Each objOSver In colOSver
        If Left(objOSver.Version,1) >= 6 Then onVistaWin7 = True
    Next
    Set colOSver = Nothing
End Function

Find a computer’s model using the command line or in a batch file

Say you’ve got a script and you want to run something in that script that only runs if you’re on a specific type of machine, what can we do to find out what machine we are on?
Some might say “use vbscript and do a wmi call”. Well yes you could do that, but that’s needlessly hard! Use this simple command instead…

wmic csproduct get name

On my machine that returns two lines, one saying Name and another with my machine’s model name: HP Compaq dc7600 Small Form Factor
How cool is that!! Basically here we’re using a WMI command line tool. There’s lots to it, just type: wmic /?

So to use this in a script we’ll need to check for the existence of a particular machine name being returned, we can use the find command a different way and return the number of lines with a specific word in it. If we get 1 or more lines with that word then we’re on the machine we’re looking for.
Here’s an answer:

wmic csproduct get name | find /c /i "7600"

On my machine that returns a 1, so we just need to parse that into a variable and use a simple if statement and we’re there:

@echo off
for /f "delims==" %%a in ('wmic csproduct get name ^| find /c /i "7600"') do set /a machine=%%a
if %machine% geq 1 (
echo Running on a 7600 machine
) else (
echo Not running on a 7600 machine
)

[script now works, thanks to the comment pointing to the pipe-char issue]

You could modify that to do a goto to jump to another part of your script I guess. Just change the "7600" part for something that uniquely identifies the machine you’re looking for in your environment.

Ths works on XP and Vista. I think it should work in WinPE too as long as you have the WMI add-in installed.

— Updated 2011-10-06 —

And now here’s a better version of my script which will help people who want to use it to batch things a bit more easily!

@ECHO OFF
REM do a wmi query to get the info we want and put it in a variable
FOR /F "tokens=2 delims==" %%A IN ('WMIC csproduct GET Name /VALUE ^| FIND /I "Name="') DO SET machine=%%A
ECHO Computer model: "%machine%"

REM Now we have the model in a variable we can do some logic and run commands, for example...
REM Watch for stray spaces at the end, take out all spaces with: SET machine=%machine: =%
IF /I "%machine%" == "Latitude E6410" (
REM do something specific for an E6410
) ELSE (
REM do something for other types
)

— Updated 2013-09-21: new version of this article using PowerShell here