Using Citrix WMI (Part 1 – Defining the function)

Posted: June 10, 2014 in Citrix, General
Tags: , , ,

I was one of countless Citrix consultants that has seen the Citrix WMI service, and kind of ignored it.  In one of my past jobs, I had found a specific use for the WMI service, but that was the only use I’d ever found for it.  (For the curious, when you use the Citrix Service Provider licensing, it requires the WMI service to be active on your servers.  It is used to to query the license for some critical information.  I had started to set up the CSP environment, and it was reporting the incorrect license type, and the IMA service would not start.  I had to start the WMI service, and then the license began to report correctly, and the IMA started).

But, I digress 🙂  In the new job, I needed to work on some scripting for our various farms (we have a mix of 4.5, 6.0, & 6.5 farms).  I had done some previous documentation type things with MFCOM in 4.5 farms, but I’d never done anything for 6.0/6.5.  I knew there were powershell cmdlets to perform a number of functions, so, that was a possibility, but I really needed to interact with all of the farms.  A coworker has a very very extensive script used to document server inventories.  I took a peek out of it out of straight curiosity, and he was using WMI calls (as expected) for everything, but to my surprise, he was using WMI calls to get Citrix data out of all 3 farms, with the same lines of code.  That definitely got my attention.

First, my coworker recommended I get Microsoft’s WMI explorer.  http://www.microsoft.com/en-us/download/details.aspx?id=24045.  This is a fantastic tool, and easily lets you connect to your local WMI providers, or remote WMI providers, and fully browse everything about them.  I’ve seen other WMI browsers, and the Microsoft one was just easier to use.  Having used this to help build my scripts, I’d strongly recommend you download it.

In my particular case, I need to query the Zone Data Collector for some information.  Generally, most environments have a dedicated machine or two to be the ZDC’s (and we are no exception).  As I was considering the design for my script, it occurred to me – what if one of those two is not going to be the ZDC at the moment my script ran?  It would fail.  It isn’t a critical function per-se, but I liked to plan to make sure my scripts will always run correctly (if at all possible).  So, I needed a way to discover the current zdc at any given moment. In theory, I could have tried to do a qfarm commend and then parsed the text, but that’s not elegant, and to run it remotely, I have to tie in something like psexec.exe or run it on one of my Citrix servers. But, I didn’t want those restrictions, so I created this powershell function script) to handle that discovery process.

The first step of course is define the function:

function get-zdc {

Because we are defining a function, and we want Powershell to automatically generate help, and support some of the common parameters, we turn on cmdlet binding.  This enables the script to act like a built-in cmdlet.  To do that, we use [CmdletBinding()].  This also has it’s own options that were not using here.

Next, we’re going to define a parameter for the function.  We need to tell it what machine to connect to in order to find the ZDC.  Because I’m dealing with lists of servers, I wanted to make sure it can handle multiple input names.  I want the function to take my list of servers and just find it.

Param(
[Parameter(mandatory=$true, valuefrompipeline=$true)]
[Alias("CN")]
[string[]]$ComputerName
)

The Param ( statement starts a list of parameters, and you only have 1 parameter block for each function.  This is required when the function is written this way.  It is possible to do an alternate structure that doesn’t use a Param block structure, but to me, that is more difficult to read.

The second line starts the definition of *a* parameter.  We’re telling it that the parameter is mandatory, and the script will throw an error if it is not provided, then we are telling it to get the value from the pipeline.  Pretty straight forward stuff, if we pipe the data into the function, it will accept it and use it.  Notice that these options are comma delimited, and are contained inside of parenthesis.   And the entire block is contained inside of brackets [ ]. (Just like the Param ()block itself, it’s not truly *required*, but it makes it easier to read.

The 3rd line defines an alias for the parameter.  This will provide a shorter/easier name to use on the command line.  Aliases are not required, but they are a nice option.  And while I didn’t use it here, you can string multiple aliases together, or you can have multiple alias blocks.  To do multiple aliases within one block, you just provide them as a comma delimited list.  I.e. [Alias(“Alias1”, “Alias2”)] (And there is not any appreciable limit to the number of aliases you can have that I’m aware of).

Lastly, we are defining the type-cast of the data we will be inputting, along with the variable name. But,this line needs a little more explanation. As I mentioned, by using the brackets, we’re defining the type of data (a string), but the important part to this is that we’re using an extra set of brackets (shown in bold) to tell the system we will accept data from the pipeline. [string[]]. Then we are giving the input parameter a name ($ComputerName). Be careful in choosing your parameter names. Many functions have ‘standardized’ parameter names for a specific reason — by using these ‘standard’ names, you can pipe input in and out of the function and the following/preceding function will accept that data in those specific parameters. (That is an entire discussion in and of itself, and as always, there are alternate techniques that can be used with it or instead of it, but it is still a best practice.

As a side note you can also add a default to a parameter just by adding an equals sign and a value. If you do that, then your function will use the default value if one is not provided. This is a great idea for a mandatory parameter, if you have something that would be appropriate. As a quick example, if I need an integer number, and I’d like the default to be the answer to life, the universe, and everything, I would use [int]$Number=42.
Also note, that within a parameter block, you can use commas to separate multiple parameters.

Param (
[parameter(mandatory=$false,valuefrompipeline=$false)]
[Alias("P1")]
[Int]$Number=42,


[parameter(mandatory=$false,valuefrompipeline=$true)]
[Alias("P2")]
[string[]]$String='Some string'
)

Notice the comma in the example after the first parameter, and the fact that the second parameter does not have that comma. (Also, be aware, there are a lot of options that can be used on the parameter statement line, or you can skip them all depending on your needs.

Next time I’ll dig into the real meat of the function.

David Figueroa

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s