Expert Solutions: Advanced Event 5 of the 2010 Scripting Games


Bookmark and Share

 

(Note: These solutions were written for Advanced Event 5 of the 2010 Scripting Games.)

 

Advanced Event 5 (Windows PowerShell)

Photo of Dr. Tobias Weltner


Dr. Tobias Weltner is a Microsoft Windows PowerShell MVP, has created the Windows PowerShell development environment PowerShellPlus (http://www.powershellplus.com), and runs one of the largest Windows PowerShell communities worldwide (http://www.powershell.com). He has written more than 100 books on computers, and his latest book about Windows PowerShell 2.0 was published by Microsoft Press Germany. Most of all, Tobias enjoys working as a trainer for quality Inhouse Windows PowerShell workshops throughout Europe. You can reach him at tobias.weltner@scriptinternals.de.

-------------

At first, retrieving video memory seems to be a trivial task: WMI has a class called Win32_VideoController with an AdapterRAM property, and to format it to MB/GB requires a simple condition in which you compare the byte value and format it to GB when it is at least 1 GB; otherwise, format it to MB.


The task has two tricky aspects, though.


First, you want to retrieve information locally and remotely from multiple computers. To be able to do this, your function needs to implement computername and credential parameters. You cannot simply hand those over to Get-WMIObject though because if the user did not specify credentials, a null value would still invoke the credentials dialog, and also credentials are not allowed on local systems. Furthermore, when you pass on lists of computers directly to Get-WMIObject, if one system is down or access denied, all other computers in that list would fail, too.


This is why the function checks whether a credential was supplied and only then calls Get-WMIObject with -Credential, and why the computername parameter is passed into -Computername via Foreach-Object, making sure Get-WMIObject processes only one computer at a time.


The second tricky aspect is that you are supposed to return the video memory as nicely formatted MB/GB, but at the same time you want to be able to compare the video size to filter out systems based on some limits. If you return a string, you no longer can compare against byte values, and if you return a number, you cannot format it.


The function solves the problem by using a composed object. It is an integer that you can easily compare against other values, but has an overridden toString() to show the nicely formatted string number. Because you might want to run the function against multiple computers, it also has custom properties and methods that tell you which system the number was retrieved from.


As a result, the function is very versatile and can be used in a number of scenarios including submitting data over the pipeline. The following image (running the function in my favorite Windows PowerShell development environment, PowerShellPlus) demonstrates the different ways you can employ this function. Note how a warning is written when systems are unavailable or access is denied, and how you can retrieve the video size or all details about the video controller. Because the result is still an integer, you can also use the function to filter out systems ready for upgrade, and because the function accepts computernames via pipeline, they can come from spreadsheets, databases, or directly from your AD.

Image of different ways to deploy function

 

Here is my script:

function Get-VideoMemory {

      param(

      [ValidateNotNullOrEmpty()]

      [Parameter(ValueFromPipeline=$true)]

      [System.String[]]

      $ComputerName = 'localhost',

      $Credential = $null

      )

 

      begin {

 

            # retrieve WMI class

            # supply credentials parameter only if credential was specified

            # make sure to call Get-WMIObject with ONE computer at a time by feeding them in via foreach-object

            # Get-WMIObject DOES support string arrays and can process more than one computer

            # but then when one call fails, all subsequent computers fail, too:

            function Get-VideoController {

                  $private:ofs = ','

                  if ($credential) {

                        $computerName | ForEach-Object { $cn = $_; try { Get-WmiObject Win32_VideoController -ComputerName $cn -Credential $credential } catch { Write-Warning "Problem with $cn : $_"} }

                  } else {

                        $computerName | ForEach-Object { $cn = $_; try { Get-WmiObject Win32_VideoController -ComputerName $cn } catch { Write-Warning "Problem with $cn : $_"} }

                  }

            }

 

            # return video memory as integer, attach videocontroller object for reference

            # select AdapterRAM property and attach original WMI videocontroller instance as new VideoAdapter property for future reference

            # add a new script method ShowDetails() to show computername and video adapter key properties

            # Note that the orginal variable type is an integer and holds the video RAM as bytes

            filter Get-VideoRAM {

                  $_.AdapterRAM |

                  Add-Member NoteProperty VideoAdapter -Value $_ -PassThru |

                  Add-Member ScriptMethod ShowDetails -Value { $this.VideoAdapter | Select-Object SystemName, VideoProcessor, VideoModeDescription, DriverVersion } -PassThru

            }

 

            # identify local system

            # Because WMI does not allow usage of credentials on local computers, and because local computers are always

            # online and do not need to be checked nor would the check work for all cases,

            # make sure you identify local systems:

            function is-Local($computername) {

                  $local = '.','localhost','127.0.0.1', $env:computername

                  $local -contains $computername

            }

 

            # filter out unavailable systems

  # this uses Test-Connection with a Count of 1 to speed it up. It uses If/Else instead of Where-Object to be able

            # to write a warning for unavailable systems. It omits local systems because Test-Connection wrongly identifies them

            # as unavailable when you are not connected to a network or use shortcuts such as ".":

            filter Test-Online {

                  if (is-Local $_) {

                        # skip online test

                        $_

                  } else {

                        $_ | ForEach-Object { if (Test-Connection -Count 1 -Quiet $_) { $_ } else { Write-Warning "Computer '$_' not reachable." } }

                  }

            }

      }

 

      # produce video memory objects, change display to formatted text

      process {

 

            # clean up submitted computers and remove those not available

            $online = $computerName | Test-Online

            if ($online -eq $null) {

                  # if none are available, skip remaining logic using "continue":

                  continue

            } else {

                  # if some or all are online, reassign cleaned list to parameter:

                  $computerName = $online

            }

 

  # use Switch as loop and condition in one. It checks all instances returned by the command in parentheses

            # it overrides the toString() method and displays video size as "cooked" value in MB/GB

            # when you output the result later, it shows the nicely formatted value

            # when you COMPARE the result later, it still is an integer in bytes

            switch (Get-VideoController | Get-VideoRAM )

            {

                  # if memory is greater or equal 1GB, show in GB...

                  {                       $_ -ge 1GB }

                  {                       $_ | Add-Member ScriptMethod toString -Value {'{0:0} GB' -f ($this / 1GB)} -force -passthru }

 

                  # ...else, show in MB...

                  default

                  {                       $_ | Add-Member ScriptMethod toString -value {'{0:0} MB' -f ($this / 1MB)} -force -passthru }

            }

      }

}

 

 

Advanced Event 5 (VBScript)

Photo of Gary Siepser

 

Gary Siepser is a senior premier field engineer (PFE) for Microsoft. He is a Microsoft Certified Master for Exchange Server 2007. As a PFE, he spends most of his time in front of Microsoft’s Premier customers delivering Windows PowerShell and Exchange

Comments (0)

Skip to main content