Hey, Scripting Guy! Can I Use WMI and Windows PowerShell to Make a Microsoft Visio Drawing?

Hey, Scripting Guy! Can I Use WMI and Windows PowerShell to Make a Microsoft Visio Drawing?

  • Comments 4
  • Likes

 Bookmark and Share

 

Hey, Scripting Guy! Question

 Hey, Scripting Guy! Using Windows PowerShell to control Microsoft Visio looks really cool. However, if I have to manually enter all of the data in the script to produce the drawing, it is probably easier to open Microsoft Visio, drag and drop the shapes from the stencils, and add the connectors. However, I will say that using the autoconnect script feature is much easier than dragging and dropping a bunch of connectors. Is there a way to integrate a Windows PowerShell script with a few Windows Management Instrumentation (WMI) queries, and use that information to make the drawing? If that could be done, I could run a script and easily document my network.

-- SA

 

Hey, Scripting Guy! AnswerHello SA,

Microsoft Scripting Guy Ed Wilson here. A day without meetings is…well, like a day without meetings. With no meetings during a day, it is amazing how productive one can be. I have Carl Weathersby cranked up on my Zune HD and am sipping a cup of English breakfast with a cinnamon stick and lemon grass. The Scripting Wife made some cranberry nut scones to go with the tea. This is the first day of the New Year on the East Coast of the United States, and it is shaping up to be a wonderful day. Because I have spent so much time outside of United States in recent years, I was unaware of what people in Charlotte do for New Year’s Eve, so the Scripting Wife and I spent last night watching a Get Smart marathon. My thoughtful present to her this year was the complete works. One of my favorite places to spend New Year’s Eve is Quebec City in Canada. I was up there for a month teaching Windows PowerShell workshops. As seen in the following image, winter up there can be very cool.

Image of winter in Quebec City


It is a relaxing day and a fun time to mellow out, spend some extra time writing scripts, and catch up on e-mail sent to scripter@microsoft.com. An ear infection is keeping me from going out to my woodworking shop or swimming, so I may as well write some scripts and answer e-mail. We can watch Get Smart later.

SA, I wrote the ComputerConfigDrawing.ps1 script for you to illustrate how to incorporate WMI data into a Microsoft Visio drawing that you generate by calling a Windows PowerShell script. The complete ComputerConfigDrawing.ps1 script is seen here.

ComputerConfigDrawing.ps1

Param($computer = "LocalHost")

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

Function Get-DesktopMonitor($computer)
{
 Get-WmiObject -Class win32_desktopmonitor -ComputerName $computer
} #end Get-DesktopMonitor

Function Get-DefaultPrinter ($computer)
{
 Get-WmiObject -Class win32_printer -ComputerName $Computer `
               -Filter "default = $true"
} #end Get-DefaultPrinter

Function Get-NetworkAdapterConfiguration ($computer)
{
 Get-WmiObject -Class win32_networkadapterconfiguration `
               -ComputerName $computer -Filter "ipenabled = $true"
} #end Get-NetworkAdapterConfiguration


$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)

$NetworkStencil = $application.Documents.Add("periph_m.vss")
$ComputerStencil = $application.Documents.Add("Computers and Monitors.vss")
$ConnectorStencil = $application.Documents.Add("Connectors.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)"

$monitorinfo = Get-DesktopMonitor -computer $computer
$Monitor = $ComputerStencil.Masters.Item("LCD monitor")
$shape2 = $page.Drop($Monitor, 5.0, 4.5)
$shape2.Text = "$($monitorinfo.name)
$($monitorinfo.ScreenHeight) x $($monitorinfo.ScreenWidth)"

$printerinfo = Get-DefaultPrinter -computer $computer
$Printer = $NetworkStencil.Masters.Item("Printer")
$shape3 = $page.Drop($Printer,6.0,3.0)
$shape3.Text = "$($printerinfo.deviceID)
$($printerinfo.PortName)`r`n$($printerinfo.Location)"

$networkinfo = Get-NetworkAdapterConfiguration -computer $computer
$Router = $NetworkStencil.Masters.Item("Router")
$shape4 = $page.Drop($Router,7.0,1.5)
$shape4.Text = "$($networkinfo.DefaultIPGateWay)"

$User = $NetworkStencil.Masters.Item("User")
$shape5 = $page.Drop($User,4.0,6.0)
$shape5.Text = "$($pcinfo.UserName)"

$etherNet = $NetworkStencil.Masters.Item("Ethernet")
$shape6 = $page.Drop($etherNet,2.2, 1.0)
$shape6.Text = "$(($networkinfo.IPAddress)[0])"

$connector = $ConnectorStencil.Masters.item("Dynamic Connector")
$shape1.AutoConnect($shape6, 0, $connector)
$shape2.AutoConnect($shape6, 0, $connector)
$shape3.AutoConnect($shape6, 0, $connector)
$shape4.AutoConnect($shape6, 0, $connector)
$shape5.AutoConnect($shape6, 0, $connector)

$page.CenterDrawing()
$document.SaveAs("C:\fso\ComputerConfig.vsd")
$application.Quit()

The first thing to do in the ComputerConfigDrawing.ps1 script is to create a command-line parameter to allow you to change the target system. Next, the Get-ComputerSystem function is used to return some information about the target computer.

For more information about functions, see the Function Week Hey, Scripting Guy! posts, or check out the Windows PowerShell 2.0 Best Practices book from Microsoft Press. For more information about using WMI and Windows PowerShell, check out the WMI Week Hey, Scripting Guy! posts.

The Win32_ComputerSystem WMI class is used with the Get-WMiObject cmdlet and the target computer supplied to the –computername parameter. The entire Win32_ComputerSystem WMI class information will be returned to the script when the Get-ComputerSystem function is called:

Param($computer = "LocalHost")

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

To get information about the monitor that is attached to the computer, the Win32_DesktopMonitor WMI class is called by the Get-DesktopMonitor function. Depending on the make of your computer and monitor, you may or may not be able to retrieve specific information about the model of your monitor:

Function Get-DesktopMonitor($computer)
{
Get-WmiObject -Class win32_desktopmonitor -ComputerName $computer
} #end Get-DesktopMonitor

When you use the Win32_Printer WMI class to return information about printers defined on a computer, you will almost always return a lot of things that are not really printers such as the Microsoft XPS Document printer, a fax machine, and even some image capturing software. I want to know about the default printer; to do this, I use the –filter parameter and limit the instances of the Win32_Printer WMI class to printers that have the default property set to $true. This is seen here:

Function Get-DefaultPrinter ($computer)
{
Get-WmiObject -Class win32_printer -ComputerName $Computer `
               -Filter "default = $true"
} #end Get-DefaultPrinter

If the bogus printers returned by the WMI class Win32_Printer are upsetting, the results returned by Win32_networkadapterconfiguration are completely out of control. On my computer, I have one real network card, and yet 12 instances are returned. One technique to reduce the number of bogus network adapters is to filter the ones that are IPenabled. This does not always work (it depends on your computer and your network configuration), but it will at least point you in the right direction:

Function Get-NetworkAdapterConfiguration ($computer)
{
Get-WmiObject -Class win32_networkadapterconfiguration `
               -ComputerName $computer -Filter "ipenabled = $true"
} #end Get-NetworkAdapterConfiguration

With the WMI functions out of the way, it is time to create a Visio drawing using some Windows PowerShell code. This portion of the script is similar to yesterday’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)

$NetworkStencil = $application.Documents.Add("periph_m.vss")
$ComputerStencil = $application.Documents.Add("Computers and Monitors.vss")
$ConnectorStencil = $application.Documents.Add("Connectors.vss")

Next, it is time to call the Get-ComputerSystem function to retrieve the computer host name and the domain. The Get-ComputerSystem function returns all of the information contained in the Win32_ComputerSystem WMI class. You could therefore modify the shape1 text property to include the additional information from that class:

$pcinfo = Get-ComputerSystem -computer $computer

After the $pcinfo variable contains the computer information from the Get-ComputerSystem function, it is time to drop a PC shape from the Computer stencil onto the Microsoft Visio drawing. Because of the way that Windows PowerShell returns WMI information, you will need to use the subexpression $() to prevent the unraveling of the object inside the expanding string (the double quotation marks). The `r`n character sequence is used to create a carriage return line feed (VBScript: vbcrlf) to continue the domain information onto the second line under the host name. This is seen here:

$pc = $ComputerStencil.Masters.Item("PC")
$shape1 = $page.Drop($pc, 2.2, 6.8)
$shape1.Text = "$($pcinfo.DNSHostName)`r`n$($pcinfo.Domain)"

The Get-DesktopMonitor function is used to retrieve the monitor information. Store the returned WMI object in the $monitorinfo variable, as seen here:

$monitorinfo = Get-DesktopMonitor -computer $computer

Next, retrieve an LCD Monitor shape from the Computer stencil and store the shape in the $monitor variable. Drop the shape onto the Microsoft Visio drawing and retrieve the monitor name property and place it on the first line. By continuing the string to the second line, you can avoid needing to use the `r`n to add a second line. The monitor resolution will be displayed as screen height by screen width on a single line. This section of the code is seen here:

$Monitor = $ComputerStencil.Masters.Item("LCD monitor")
$shape2 = $page.Drop($Monitor, 5.0, 4.5)
$shape2.Text = "$($monitorinfo.name)
$($monitorinfo.ScreenHeight) x $($monitorinfo.ScreenWidth)"

Next, the Get-DefaultPrinter function is used to retrieve information about the default printer and store it in the $printerinfo variable:

$printerinfo = Get-DefaultPrinter -computer $computer

The Printer shape comes from the Network Peripherals stencil and is stored in the $printer variable. The name, port name, and location properties are added to the text property of shape3, as seen here:

$Printer = $NetworkStencil.Masters.Item("Printer")
$shape3 = $page.Drop($Printer,6.0,3.0)
$shape3.Text = "$($printerinfo.deviceID)
$($printerinfo.PortName)`r`n$($printerinfo.Location)"

Network information is retrieved by the Get-NetworkAdapterConfiguration function and stored in the $networkinfo variable:

$networkinfo = Get-NetworkAdapterConfiguration -computer $computer

The Router shape is retrieved from the Network Peripherals stencil and stored in the $router variable. The IP address of the router comes from the defaultIPGateway property from the Win32_networkadapterconfiguration WMI class. This is shown here:

$Router = $NetworkStencil.Masters.Item("Router")
$shape4 = $page.Drop($Router,7.0,1.5)
$shape4.Text = "$($networkinfo.DefaultIPGateWay)"

A User stencil is obtained from the Network Peripherals stencil and stored in the $user variable. The user name of the current user is obtained from the computer information that was stored in the $pcinfo variable earlier. This section of the script is seen here:

$User = $NetworkStencil.Masters.Item("User")

$shape5 = $page.Drop($User,4.0,6.0)
$shape5.Text = "$($pcinfo.UserName)"

The IP address of the workstation is retrieved from the network information stored in the $networkinfo variable. The IPAddress property of the Win32_NetworkAdapterConfiguration WMI class is an array on Windows Vista and later that returns both IPV4 and IPV6 information. The IPV4 address is stored in element 0 of the array. For this script, we are ignoring the IPV6 address that is stored in element 1 of the array. This is assigned to the text property of the Ethernet shape, as shown here:

$etherNet = $NetworkStencil.Masters.Item("Ethernet")
$shape6 = $page.Drop($etherNet,2.2, 1.0)
$shape6.Text = "$(($networkinfo.IPAddress)[0])"

All that remains is to connect the shapes, center the drawing, save, and close everything. This is the same as the code used in yesterday’s Hey, Scripting Guy! post.

$connector = $ConnectorStencil.Masters.item("Dynamic Connector")
$shape1.AutoConnect($shape6, 0, $connector)
$shape2.AutoConnect($shape6, 0, $connector)
$shape3.AutoConnect($shape6, 0, $connector)
$shape4.AutoConnect($shape6, 0, $connector)
$shape5.AutoConnect($shape6, 0, $connector)

$page.CenterDrawing()

$document.SaveAs("C:\fso\ComputerConfig.vsd")
$application.Quit()

When the ComputerConfigDrawing.ps1 script runs, the following drawing is displayed.

Image of drawing displayed when script is run


SA, that is all there is to using Microsoft Visio to create a drawing of your computer configuration. The script for today can also be found at the Script Repository.  Microsoft Visio Week will continue tomorrow.

If you want to know exactly what we will be looking at tomorrow, follow us on Twitter or Facebook. If you have any questions, send e-mail to us at scripter@microsoft.com or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.
 

Ed Wilson and Craig Liebendorfer, Scripting Guys 

 

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • I think there is a typo in the sript.

    This:

    $shape6.Text = "$(($networkinfo.IPAddress)[0])"

    Should have been this:

    $shape6.Text = "$($networkinfo[0].IPAddress)"

    That is, get the IP address of the first entry in the results from get-networkadapterconfiguration

  • Great tutorial!!!

    I would like to add some values to the shape data, for example on the pc stencil there are some values like (Serial number, room, building, etc...).

    Can I add some values programatically to this stencil?? How can i do it?

  • Very useful indeed.

    For holding shape data i use an Excel file where a row contains the data for each shape, first row holding column headings. In Visio you can then add the shape data as a recordset:

    $pathToShapeData = "C:\Data\ShapeData.xlsx"

    $connection = "Provider=Microsoft.ACE.OLEDB.12.0;User ID=Admin;Data Source=$pathToShapeData;Mode=Read;" + 'Extended Properties="HDR=YES;IMEX=1;MaxScanRows=0;Excel 12.0;";' + "Jet OLEDB:Engine Type=34;" ### Note, one long line...

    $command = "SELECT * FROM [Sheet1$]"

    $recordSet = $document.DataRecordsets.Add($connection, $command, 0, "ShapeData")

    Once you have your recordset you can link your shapes to  the data:

    $shape.LinkToData($recordSet.ID , $recordsetRowID, $false)

    where  $recordsetRowID  corresponds to the c0 property value of the record.

    If the data changes in excel you can just refresh the data and have the shape properties reflect the updated value:

    $recordSet.Refresh() ### or do the refresh manually in Visio...

    For further reference see msdn.microsoft.com/.../ff767582%28v=office.14%29.aspx and

    msdn.microsoft.com/.../ff766962%28v=office.14%29.aspx

  • thanks