January 14th, 2010

Hey, Scripting Guy! How Can I Create a Windows Visio Drawing of My Workstations' Disk Subsystem?

Bookmark and Share

 

Hey, Scripting Guy! Question

Hey, Scripting Guy! I would like to do a drawing of the disk subsystem on my workstations. We are getting ready to upgrade to Windows 7, and I want to find out which computers have drives that are more than 50 percent full. In addition, I need to know the size of the drives. If I could make a Microsoft Visio drawing using Windows PowerShell, it would be awesome. Is this something that is possible?

— JB

 

Hey, Scripting Guy! AnswerHello JB,

Microsoft Scripting Guy Ed Wilson here. I have been slowly catching up on e-mail that was sent to the scripter@microsoft.com alias, and when I ran across your letter, I thought the idea was pretty cool. I spent most of the day writing the ComputerDiskDrawing.ps1 script seen here.

ComputerDiskDrawing.ps1

Param($computer = “LocalHost”)

# *** Functions ***
Function Get-ComputerSystem ($computer)
{
 Get-WmiObject -Class win32_computersystem -ComputerName $computer
} #end Get-ComputerSystem

Function Get-Volumes($computer)
{
 Get-WmiObject -class win32_Volume -filter “DriveType = 3” `
    -computername $computer
} #end Get-Volumes

Function Get-OptimalSize
{
 <#
  .Synopsis
    Converts Bytes into the appropriate unit of measure.
   .Description
    The Get-OptimalSize function converts bytes into the appropriate unit of
    measure. It returns a string representation of the number.
   .Example
    Get-OptimalSize 1025
    Converts 1025 bytes to 1.00 KiloBytes
    .Example
    Get-OptimalSize -sizeInBytes 10099999
    Converts 10099999 bytes to 9.63 MegaBytes
   .Parameter SizeInBytes
    The size in bytes to be converted
   .Inputs
    [int64]
   .OutPuts
    [string]
   .Notes
    NAME:  Get-OptimalSize
    AUTHOR: Ed Wilson
    LASTEDIT: 1/4/2010
    KEYWORDS:
   .Link
     Http://www.ScriptingGuys.com
 #Requires -Version 2.0
 #>
 Param([int64]$sizeInBytes)
 Switch ($sizeInBytes)
  {
   {$sizeInBytes -ge 1TB} {“{0:n2}” -f  ($sizeInBytes/1TB) + ” TeraBytes”;break}
   {$sizeInBytes -ge 1GB} {“{0:n2}” -f  ($sizeInBytes/1GB) + ” GigaBytes”;break}
   {$sizeInBytes -ge 1MB} {“{0:n2}” -f  ($sizeInBytes/1MB) + ” MegaBytes”;break}
   {$sizeInBytes -ge 1KB} {“{0:n2}” -f  ($sizeInBytes/1KB) + ” KiloBytes”;break}
   Default { “{0:n2}” -f $sizeInBytes + ” Bytes” }
  } #end switch
} #end Function Get-OptimalSize

# *** Entry to Script ***

$application = New-Object -ComObject Visio.Application
$application.visible = $false
$documents = $application.Documents
$document = $documents.Add(“Basic Network Diagram.vst”)
$pages = $application.ActiveDocument.Pages
$page = $pages.Item(1)

$ComputerStencil = $application.Documents.Add(“Computers and Monitors.vss”)
$BasicStencil = $application.Documents.Add(“Basic Shapes.vss”)

$pcinfo = Get-ComputerSystem -computer $computer
$pc = $ComputerStencil.Masters.Item(“PC”)
$shape1 = $page.Drop($pc, 2.2, 6.8)
$shape1.Text = “$($pcinfo.DNSHostName)`r`n$($pcinfo.Domain)”

$Volumes = Get-Volumes -computer $computer
$x = 3.0 #establishes left column
$xx = 6.0
$y = 6.0 #establishes right column
$yy = 7.0
$visSectionObject = 1
$visRowFill = 3
$visFillForegnd = 0
ForEach($volume in $Volumes)
{
 $inuse = $volume.capacity – $volume.freeSpace
 $percentInUse = ($inuse / $volume.Capacity) * 100
 $shape2 = $page.DrawRectangle($x,$xx,$y,$yy)
 $shape2.text = “DRIVE: $($Volume.name)
  Capacity: $(Get-OptimalSize -sizeinbytes $($Volume.Capacity))
  In Use: $(Get-OptimalSize -sizeinbytes $inuse)”
  if($percentInUse -gt 50)
    {
     $shape2.CellsSRC($visSectionObject,$visRowFill, `
                      $visFillForegnd).FormulaU=”THEMEGUARD(RGB(255,0,0))”
    }
  else
    {
     $shape2.CellsSRC($visSectionObject, $visRowFill, `
                      $visFillForegnd).FormulaU=”THEMEGUARD(RGB(0,255,0))”
    }
 $xx -= 1.2
 $yy-= 1.2
}
$visCharacterStyle = 2
$visRowCharacter = 0
$visSectionCharacter = 3

$ColumnHead = $page.DrawRectangle(3.54,7.08,5.51,7.48)
$ColumnHead.TextStyle = “Normal”
$ColumnHead.LineStyle = “Text Only”
$columnHead.FillSTyle = “Text Only”
$ColumnHead.Text = “DISK DRIVES”
$ColumnHead.CellsSRC($visSectionCharacter,$visRowCharacter, `
                     $visCharacterStyle).FormulaU = 1

$page.CenterDrawing()
$document.SaveAs(“C:fsoComputerDisks.vsd”)
$application.Quit()

The first thing the ComputerDiskDrawing.ps1 script does is create a command-line parameter for the name of the target computer and a couple of functions that are used to retrieve WMI information. The Get-ComputerSystem function uses the Win32_ComputerSystem WMI class to retrieve basic computer information and the Get-Volumes function uses the Win32_Volume WMI class, which is available beginning with Windows Server 2003, to return information about fixed local disks on the computer. This section of the script is shown here:

Param($computer = “LocalHost”)
Function Get-ComputerSystem ($computer)
{
Get-WmiObject -Class win32_computersystem -ComputerName $computer
} #end Get-ComputerSystem
Function Get-Volumes($computer)
{
Get-WmiObject -class win32_Volume -filter “DriveType = 3” `
    -computername $computer
} #end Get-Volumes

It is time to create a function that will translate the disk sizes (returned in bytes) to a more easily understood size. Windows PowerShell has several built in constants that can be used to translate numeric values from bytes to kilobytes (KB), megabytes (MB), gigabytes (GB), or even terabytes (TB). To translate 1,024 bytes into kilobytes, you can use the KB constant, as seen here:

PS C:> 1024 / 1KB
1
PS C:>

Inside the Get-OptimalSize function, the Switch statement is used to evaluate the value of the –sizeinbytes parameter and to determine the correct unit to display the value. One interesting thing that is done inside the Switch statement is the use of a script block for each condition to evaluate. This technique is not documented in the help files. The reason a script block is required is to perform an evaluation of the size of the number stored in the sizeinbytes parameter. If the value is greater than or equal to 1 terabyte, the number is translated into terabytes. The “{0:n2}” –f construction is a format specifier to limit the number that is returned to two decimal places. The complete switch statement is seen here:

Switch ($sizeInBytes)
  {
   {$sizeInBytes -ge 1TB} {“{0:n2}” -f  ($sizeInBytes/1TB) + ” TeraBytes”;break}
   {$sizeInBytes -ge 1GB} {“{0:n2}” -f  ($sizeInBytes/1GB) + ” GigaBytes”;break}
   {$sizeInBytes -ge 1MB} {“{0:n2}” -f  ($sizeInBytes/1MB) + ” MegaBytes”;break}
   {$sizeInBytes -ge 1KB} {“{0:n2}” -f  ($sizeInBytes/1KB) + ” KiloBytes”;break}
   Default { “{0:n2}” -f $sizeInBytes + ” Bytes” }
  } #end switch

The complete Get-OptimalSize function is shown here:

Function Get-OptimalSize
{
<#
  .Synopsis
    Converts Bytes into the appropriate unit of measure.
   .Description
    The Get-OptimalSize function converts bytes into the appropriate unit of
    measure. It returns a string representation of the number.
   .Example
    Get-OptimalSize 1025
    Converts 1025 bytes to 1.00 KiloBytes
    .Example
    Get-OptimalSize -sizeInBytes 10099999
    Converts 10099999 bytes to 9.63 MegaBytes
   .Parameter SizeInBytes
    The size in bytes to be converted
   .Inputs
    [int64]
   .OutPuts
    [string]
   .Notes
    NAME:  Get-OptimalSize
    AUTHOR: Ed Wilson
    LASTEDIT: 1/4/2010
    KEYWORDS:
   .Link
     Http://www.ScriptingGuys.com
#Requires -Version 2.0
#>
Param([int64]$sizeInBytes)
Switch ($sizeInBytes)
  {
   {$sizeInBytes -ge 1TB} {“{0:n2}” -f  ($sizeInBytes/1TB) + ” TeraBytes”;break}
   {$sizeInBytes -ge 1GB} {“{0:n2}” -f  ($sizeInBytes/1GB) + ” GigaBytes”;break}
   {$sizeInBytes -ge 1MB} {“{0:n2}” -f  ($sizeInBytes/1MB) + ” MegaBytes”;break}
   {$sizeInBytes -ge 1KB} {“{0:n2}” -f  ($sizeInBytes/1KB) + ” KiloBytes”;break}
   Default { “{0:n2}” -f $sizeInBytes + ” Bytes” }
  } #end switch
} #end Function Get-OptimalSize

The next section of the script is the same as Wednesday’s Hey, Scripting Guy! post:

$application = New-Object -ComObject Visio.Application
$application.visible = $false
$documents = $application.Documents
$document = $documents.Add(“Basic Network Diagram.vst”)
$pages = $application.ActiveDocument.Pages
$page = $pages.Item(1)
$ComputerStencil = $application.Documents.Add(“Computers and Monitors.vss”)
$BasicStencil = $application.Documents.Add(“Basic Shapes.vss”)
$pcinfo = Get-ComputerSystem -computer $computer
$pc = $ComputerStencil.Masters.Item(“PC”)
$shape1 = $page.Drop($pc, 2.2, 6.8)
$shape1.Text = “$($pcinfo.DNSHostName)`r`n$($pcinfo.Domain)”

Store the management objects that are returned by the Get-Volumes function in the $volumes variable, as seen here:

$Volumes = Get-Volumes -computer $computer

Several variables are created to establish the initial coordinates for drawing the rectangle. These are seen here:

$x = 3.0 #establishes left column
$xx = 6.0
$y = 6.0 #establishes right column
$yy = 7.0

Three additional variables are created that hold enumeration values for the CellsSRC method that will be used to add color to the rectangle. The first variable, $visSectionObject, holds the VisSectionIndices enumeration value; the second variable, $visRowFill, holds the VisRowIndices enumeration value; and the third variable, $visFillForegnd, holds the VisCellIndices enumeration value. The value assignments are seen here:

$visSectionObject = 1
$visRowFill = 3
$visFillForegnd = 0

When all of the appropriate variables have been created and assigned values, it is time to loop through the collection of Win32_Volume objects that are stored in the $volumes variable. The $volume variable holds a complete object that represents one of the fixed local disks on the computer. The amount of occupied disk space is computed by subtracting the freeSpace from the capacity of the drive. The result is stored in the $inuse variable. Next, the percentage of disk utilization is computed by using the value stored in the $inuse variable and the capacity. The result is stored in the $percentInUse variable. This is seen here:

ForEach($volume in $Volumes)
{
$inuse = $volume.capacity – $volume.freeSpace
$percentInUse = ($inuse / $volume.Capacity) * 100

The DrawRectangle method is used to draw a rectangle on the Microsoft Visio document. The first two arguments to the DrawRectangle method of the page object represent the lower left corner of the rectangle, and the last two arguments to the method represent the upper right corner of the rectangle. The returned shape object is stored in the $shape2 variable seen here:

 $shape2 = $page.DrawRectangle($x,$xx,$y,$yy)

The text property of the shape object is used to display the drive name, capacity, and amount of space that is in use. The Get-OptimalSize function is used to choose the appropriate units of measure for each drive:

 $shape2.text = “DRIVE: $($Volume.name)
 Capacity: $(Get-OptimalSize -sizeinbytes $($Volume.Capacity))
 In Use: $(Get-OptimalSize -sizeinbytes $inuse)”

If the drive is more than 50 percent full, the rectangle representing the drive is displayed in red. To do this, the CellsSRC property is used to apply a red-green-blue (RGB) value. When the RGB value is 255,0,0, it is all red. If the drive is less than 50 percent full, the rectangle representing the drive is displayed in green. Green is 0,255,0 in RGB nomenclature. This section of the script is seen here:

if($percentInUse -gt 50)
    {
     $shape2.CellsSRC($visSectionObject,$visRowFill, `
                      $visFillForegnd).FormulaU=”THEMEGUARD(RGB(255,0,0))”
    }
  else
    {
     $shape2.CellsSRC($visSectionObject,$visRowFill, `
                      $visFillForegnd).FormulaU=”THEMEGUARD(RGB(0,255,0))”
    }

Because the script loops around creating rectangles for each volume on the computer system, the value of the $xx and $yy variables is decremented by 1.2. This will lower the position of the next rectangle to be created by 1.2. The -= operator is used to subtract and assign a new value to the existing value of the $xx and $yy variables respectively. Using the -= operator is illustrated here:

PS C:> $a = 5
PS C:> $a -= 3
PS C:> $a
2
PS C:>

The code to decrement the value of the two variables is seen here:

 $xx -= 1.2
$yy-= 1.2
}

Now three more variable assignments need to be made. These variables will be used with the b property. The first variable, $visCharacterStyle, holds the VisSectionIndices enumeration value. The second variable, $visRowCharacter, holds the VisRowIndices enumeration value. And the third variable, $visSectionCharacter, holds the VisCellIndices enumeration value. This is seen here:

$visCharacterStyle = 2
$visRowCharacter = 0
$visSectionCharacter = 3

The DrawRectangle method from the page object is used to create a text box that will be used for the column head. The resulting shape is stored in the $ColumnHead variable:

$ColumnHead = $page.DrawRectangle(3.54,7.08,5.51,7.48)

After creating the shape, the TextStyle property is set to normal, and the LineStyle and FillStyle properties are set to Text Only. The resulting shape will only display the value that is assigned to the Text property of the shape. This section of the script is seen here:

$ColumnHead.TextStyle = “Normal”
$ColumnHead.LineStyle = “Text Only”
$columnHead.FillStyle = “Text Only”
$ColumnHead.Text = “DISK DRIVES”

It is time to use the CellsSRC property to assign the FormulaU the value of 1. This will cause the text to appear in bold:

$ColumnHead.CellsSRC($visSectionCharacter,$visRowCharacter, `
                     $visCharacterStyle).FormulaU = 1

After adding the column heading, all that remains is to center the drawing, save the drawing, and exit Microsoft Visio. The code to do this is the same we have used all week:

$page.CenterDrawing()
$document.SaveAs(“C:fsoComputerDisks.vsd”)
$application.Quit()

When the script runs, the following drawing is saved to the destination supplied to the SaveAs method of the document object.

Image of drawing created when script is run

 

JB, that is all there is to using Microsoft Visio to create a diagram of your disk drives. The script for today can also be found at the Script Repository.  This concludes the Microsoft Visio Week articles. Join us tomorrow for Quick-Hits Friday.

If you want to know exactly what we will be discussing tomorrow, follow us on Twitter or Facebook

Author

0 comments

Discussion are closed.