Expert Solution for 2011 Scripting Games Advanced Event 5: Use PowerShell to Get Information Prior to Windows Upgrade

Expert Solution for 2011 Scripting Games Advanced Event 5: Use PowerShell to Get Information Prior to Windows Upgrade

  • Comments 6
  • Likes

Summary: Microsoft MVP, Richard Siddaway, solves 2011 Windows PowerShell Scripting Games Advanced Event 5 and gets information prior to a Windows upgrade.

Microsoft Scripting Guy, Ed Wilson, here. Richard Siddaway is our expert commentator today. Here are Richard’s own words describing his background, followed by his solution to Advanced Event 5.

Photo of Richard Siddaway

I have been working with Microsoft technologies for over 22 years, having spent time in most IT roles including analyst-programmer, server administration, support, DBA, and architect. I have been interested in automation techniques (including automating job creation and submission on mainframes many years ago). I have been using VBScript and WMI since it became available on Windows NT 4. Windows PowerShell caught my interest when I first heard about it, and I have been using it since the early beta versions. My blog is mainly about Windows PowerShell. I founded and run the UK PowerShell user group, and I am a Windows PowerShell MVP. I have given numerous talks on Windows PowerShell at various events, and I have had a number of articles about Windows PowerShell published. PowerShell in Practice (Manning publications) was published in June 2010 and I am currently writing PowerShell and WMI (Manning).

Richard's contact information:
Blogs:
RichardSiddaway's Blog of PowerShell and Other Things
IT Knowledge Exchange

Worked solution

The email arrives. Scripting Guy says, “This is your event.” Open the attached document. OK, Advanced Event 5. What do we have to do?

Your company is planning an upgrade from Windows XP to Windows 7 and you get to write a script that will determine if a computer meets the requirements for upgrade. We have a bunch of criteria to worry about to decide if we can upgrade:

  • CPU
  • Memory
  • Disk
  • Video driver

Leave them for now. We also have a bunch of design points:

  • Your solution should work on multiple computers.
  • Your solution should accept pipelined input for the computer names.
  • You should return a single object for each computer.
  • Your code should be reusable.
  • Your solution should have comment-based Help.
  • Your solution should use error handling.
  • Your solution should implement the Debug switch.
  • Extra design points if your solution will detect if the computer in question would be best upgraded to 64-bit (x64) Windows 7 or 32-bit (x86) Windows 7.

If we are looking at multiple computers, we need to work remotely. We do not want to drag around lots of machines while we test. We cannot use Windows PowerShell remoting because we do not know if Windows PowerShell is installed on the remote machines. That leaves WMI.

When I am working with WMI, my first job is always to work out which classes and properties I need. CPU, memory, and disk are straightforward. The requirement to check if the video unit supports WDDM 1.0 or above is a new one for me. A quick search shows that checking the version of the driver should work. This gives me:

## architecture 0 = x86 & 9 = x64

## current clock speed in MHz

Get-WmiObject -Class Win32_Processor | select Architecture, CurrentClockSpeed

Architecture is an integer code, so I need to develop a look-up table. It is better to use MaxClockSpeed because CurrentClockSpeed can vary. The speed is in MHz, and it can remain like that with no problems.

We create the look-up table like this:

$cpuArch = DATA {

ConvertFrom-StringData -StringData @'

0 = x86

9 = x64

'@

}

The calculated field looks like this:

@{N="CPUArchitecture"; E={$cpuArch["$($_.Architecture)"] }},

N is short for Name and E is short for Expression.

## total physical memory is in bytes, but it doesn't show what is used by display:

Get-WmiObject -Class Win32_ComputerSystem | select TotalPhysicalMemory

TotalPhysicalMemory is in bytes, so we need to turn it into GB. A trusty calculated field in a select statement takes care of that. This measures the total memory when the system is running—it subtracts the memory used for video if the machine grabs some of the system memory for graphics.

## capacity is the amount of memory in each module.

Get-WmiObject -Class Win32_Physicalmemory | Measure-Object -Sum Capacity

Win32_PhysicalMemory returns one object per installed memory module. This is the raw amount of memory. We need to sum across all memory modules. Measure-Object is made for this.

## drive type 3 is hard drive, check size, and freespace. Can't do a direct upgrade to Win 7.

## so size works.

Get-WmiObject Win32_LogicalDisk -Filter "DriveType=3" | Select DeviceId, Size, Freespace

I decided to change this a bit, so I get all hard disks, then sort descending on size and select the first disk. This gives me the biggest disk to test. We cannot do a straight Windows XP to Windows 7, so we will be reinstalling programs.

## needs to be 7 or above to be WDDM.

Get-WmiObject Win32_VideoController | select DriverVersion

The driver is of the form 8.15.11.8644. We only need the first digit so we can split the string to test it, but we cannot do the following:

$computerdata.DriverVersion -split "."

We need to escape the “.” because it is treating the split character as a regular expression. We need to use the following syntax.

$computerdata.DriverVersion -split "\."

Or we could do something like the following, where 0 says return all and “SimpleMatch” interprets the “.” literally.

$computerdata.DriverVersion -split ".",0,"SimpleMatch"

That is the easy bit done. Can I stop now? OK—to continue…

Looking at the other design criteria:

  • Your code should be reusable.

Why would I want to reuse this code? I am frequently involved in upgrades such as SQL Server, Exchange, or Active Directory. I might want to be able to test other systems for different criteria. I decided to create a Windows PowerShell module. I have imaginatively called it adv5 for now, and I will rename it later. At present, the module has five functions. The names should explain their function:

  • Test-Win7Upgrade
  • get-cputype
  • get-computersystem – gets available memory
  • get-totalram
  • get-discsize
  • get-videodriver

Only the Test-Win7Upgrade function is visible when the module is loaded because I restrict the export by using the following:

Export-ModuleMember -Function Test-Win7Upgrade

The other functions are available to my module, but not generally.

  • Your solution should work on multiple computers.
  • Your solution should accept pipelined input for the computer names.

This means that we need to be able to pipe in multiple names on the command line or we put the names in a csv file and pipe them in by using Import-Csv. We can take care of this in the following param block:

param (

 [parameter(ValueFromPipeline=$true,

   ValueFromPipelineByPropertyName=$true)]

 [string]$computer="."

 

)

By using a parameter and telling it to accept values from the pipeline, we accomplish this bit. Just in case I change my mind about the helper functions being hidden, I made them accept pipeline input as well.

  • You should return a single object for each computer.

I split my main function into Begin/Process/End blocks. In the Begin block, I use some inline C# code to define a new class and Add-Type to compile it.

Begin {

 

$source=@"

public class UpgradeTest

{

  public string ComputerName    {get; set;}

  public string CPUArchitecture {get; set;}

  public int    CPUSpeed        {get; set;}

  public double AvailableMemory {get; set;}

  public double TotalMemory     {get; set;}

  public double DiskSize        {get; set;}

  public string DriverVersion   {get; set;}

  public bool   Upgradable      {get; set;}

  public string Recommend       {get; set;}

}

"@

 

Add-Type -TypeDefinition $source -Language CSharpversion3

 

$data = @()

} # end begin block

I have also put comments against the closing brace so I can trace where I am in the script. I can then create an object and populate it by using New-Object as follows:

$computerdata = New-Object -TypeName UpgradeTest -Property @{

  ComputerName    = $computer

  CPUArchitecture = $cpu.CPUArchitecture

  CPUSpeed        = $cpu.MaxClockSpeed

  AvailableMemory = $system.AvailableMemory

  TotalMemory     = $ram.TotalMemory

  DiskSize        = $disk.Size

  DriverVersion   = $video.DriverVersion

 }

  • Your solution should have comment-based Help.

Comment-based Help looks like this (abbreviated):

<#

.SYNOPSIS

 Tests a computer to determine if it can be upgraded to Windows 7

 

.DESCRIPTION

 Tests are performed on

  - CPU speed and architecture

  - Memory

  - Disk Size

  - Video driver

To determine if a system can be upgraded to Windows 7, a recommendation is made as to whether the system should be upgraded to Windows 7 32 bit or 64 bit.

.PARAMETER  Computer

Name of computer to be tested. Can be NetBIOS name or IP Address

 

.EXAMPLE

.EXAMPLE

 

.INPUTS

String containing computer name or IP address

 

.OUTPUTS

 

#>

It is a block comment with the keyword preceded by “.”. See about_comment_based_help for full details.

  • Your solution should use error handling.

I decided to test that:

  • I can ping the machine using Test-Connection.
  • None of the variables returned by my WMI calls are NULL.

If either of these are triggered, appropriate messages are displayed.

  • Your solution should implement the Debug switch.

This is the cheapest piece of functionality to implement. Put the following before the param block and a Debug switch is implemented.

[CmdletBinding()]

Use Write-Debug to display the messages and put the function in debug mode like this:

Test-Win7Upgrade -computer 127.0.0.1 -Debug

  • Extra design points if your solution will detect if the computer in question would be best upgraded to 64-bit (x64) Windows 7 or 32-bit (x86) Windows 7.

After testing that the machine can be upgraded, I look at if it is a 64-bit capable CPU. An x86 architecture CPU can only have 32-bit Windows. If it is x64 architecture, I test the memory and disk space against the 64-bit tests. If it passes, a recommendation is made for 64-bit; otherwise, it’s doomed to being relegated to a lowly 32-bit machine.

The full script can be found in the Script Repository. Enjoy the rest of the games.

Thank you for your solution, Richard.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • The full listing can be found at gallery.technet.microsoft.com/.../6c86b8cf-0302-4cdf-939a-5c9fbfc104e5

  • @Richard  Thank you again for writing this solution and publishing the script to the script repository.

  • I found during testing that the Win32_PhysicalMemory class does not always return results;  I tested my version of the script against a server--yes I know, workstations only, but that is what I had to test on.  My server's Tyan board didn't seem to have support for that WMI class so I had to pick an alternative way to get the information.  If I recall, there is another WMI class that has a physical memory quantity that can be used.  There could well be client motherboards that don't support the PhysicalMemory class so keep this in mind if you don't get results.

  • That confirms my best guess on the driver version :)  I was wondering if that was the best way to get WDDM versions since dxdiag looked all over the place and hard to get it to run remotely in a reliable way.

  • Dear Richard,

    a very good and detailed solution to the event!

    The code is clear and I could easily follow your thoughts.

    One thing left to mention as usual: I read what you said to error handling but I will still get errors tyring to issue a "gwmi" on a remote computer that is not "mine" Without admin rights you have to take care of every little bit :-(

    kind regrads, Klaus (Schulte)

  • thank you