Jump to content
Software FX Community

Writing refreshable scripts for PowerGadgets


Recommended Posts

Although most scripts should refresh fine in PowerGadgets, there are some things you need to be aware of when writing those scripts. In order to debug your script refreshability (not sure this is a word but it sounds ok), I would recommend not using -refresh but instead right click the chart/gauge/map and select Refresh. Once your script is working you will obviously use -refresh with the desired refresh rate. Also if you have a Vista machine handy you may want to test there. In the current bits we show a MessageBox in XP but in Vista you can click "See Details" to get a little more information about any exceptions we encounter.

Security Issues

To illustrate the security issues you may experience I will write a script that gets some information about IIS servers using WMI. This is very similar to this post although to focus on drilldown I made myself an admin on my previous post to avoid any credentials issues. This time I am not an admin so to connect to IIS6 using WMI we need to authenticate ourselves.

Let's start with a hardcoded script (HardcodedIIS.ps1)

$serverName = "webserver1"
Get-WmiObject Win32_PerfFormattedData_W3SVC_WebService -computername $serverName -filter "Name='_Total'" -property __SERVER, CurrentConnections -credential yourDomain\Administrator | select CurrentConnections

If you execute HardCodedIIS you will get a powershell prompt requesting the password for the Administrator account, if the password is accepted you will get the current connections for all the sites in your web server.

At this point if you execute HardCodedIIS | out-chart you will get a bar chart but if you click Refresh you will get prompted again. Although this doesn't sound too good, note that any subsequent refreshes will not prompt again as we cache the credential and reuse it for the life of this particular gadget. To try to fix this we implemented a parameter in newer builds (tentatively called -CacheCredential), when you use this parameter we try to reuse the credentials but it only works under the following circumstances

a) You use a variable of type credential
B) The "command line" that precedes out-chart uses this credential as a parameter on a cmdlet and not a script

Imagine you type the following in an interactive powershell session

$cred = get-credential softwarefx\Administrator
Get-WmiObject Win32_PerfFormattedData_W3SVC_WebService -computername WebServer1 -filter "Name='_Total'" -property __SERVER, CurrentConnections -credential $cred | select CurrentConnections | out-chart -CacheCredential

The reason for (a) is that in order to implement refresh we have to reexecute the command that precedes us (note that in this case is the get-wmiobject) and we can see that you are using a variable of type credential, this is unfortunate as -credential domain\Admin does not work but I would not consider this a showstopper.
Reason (B) on the other hand is definitely a showstopper, if you try to put the "get-wmiobject ... | select CurrentConnections" line in a script that receives one param you will see that again we reprompt the credentials once more. It seems to me that PowerShell does not treat equally a cmdlet that exposes a PSCredential parameter from a script that declares a param of type PSCredential. I can only guess that this is because we cannot adorn the script parameter with the [Credential()] attribute as in the cmdlet but only somebody from the PowerShell team would know if this is in fact the case.

Bottom line, when using authentication you will get authenticated once more the first time a gadget is refreshed. We will try to fix this in future builds.

Read Host

One of the obvious improvements you would do to HardCodedIIS would be to prompt both for the server name and account as follows, this new script (PromptIIS.ps1) is much better as you can now use it for any web servers in your domain.

param ([string]$serverName = $(read-host "Please enter the web server name"), [string]$account = $(read-host "Please enter your domain\username account"))
Get-WmiObject Win32_PerfFormattedData_W3SVC_WebService -computername $serverName -filter "Name='_Total'" -property __SERVER , CurrentConnections -credential (get-credential $account) | select __Server , CurrentConnections

If you try now to do PromptIIS | out-chart and hit refresh you will get an error "Failed to Connect to Data" and the details will say "Cannot invoke this function because the current host does not implement it". In the future we may implement read-host and show a dialog prompting for the required parameters but hopefully what you want is for the current server name and account to be reused. In order to achieve this you will have to create a separate PromptIISChart.ps1 as follows

param ([string]$serverName = $(read-host "Please enter the web server name"), [string]$account = $(read-host "Please enter your domain\username account"))
PromptIIS -serverName $serverName -account $account | out-chart

When running PromptIISChart you will get the prompt for the parameters but the refresh command will include the parameters you used, because of this PromptIIS will not need to reprompt and refresh will work as expected (although you will have to provide your password once more).

Separate the data from the presentation in your scripts 

There are 2 main reasons why we think this is a good idea, first it allows you to reuse your scripts in several situations, e.g. if you have a script that returns a collection of objects, you can pipe them to out-chart to see trends or you can use measure-object to count the items and pass it to out-gauge. Additionally you can invoke your script and pipe it to where/sort/etc before passing it to the chart

The second reason is a technical one, when refreshing we only reexecute the command that precedes our cmdlets in the same line, this can be illustrated in the following pseudo script

$data1 = get-cmdlet1 ...
$data2 = get-cmdlet2 -param $data1
get-cmdlet3 -param $data2 | out-chart

If you try to refresh this chart, we are only aware of "get-cmdlet3 -param $data2" and this will only work if $data2 is a string or number but not if it is an object. Note that we will NOT try to execute the get-cmdlet1 and -get-cmdlet2 calls are we are not even aware of them. Because of this you will have to break this up in two scripts. This means that your data script will have all the get-cmdlet* calls so all of them will be reexecuted when you type DataScript | out-chart


This post was inspired by the OpsMgr script written by Stefan Stranger and in particular this thread.


Link to comment
Share on other sites

  • Create New...