View all posts filed under 'Citrix'

How To Remotely Administer A Citrix Farm From The PowerShell Prompt?

Thursday, 10. June 2010 14:15

If you’re using Windows PowerShell to administrer and automate Citrix Farms the day will dawn when you ask yourself (or someone else) if it is possible to fire up the locally installed Windows PowerShell Console in order to perform a task against a Citrix Farm remotely. Yes, it is possible

In case of XenApp 6 you make use of PowerShell 2.0′s Remoting feature as follows:

#
# Remote XenApp 6 w/ PowerShell 2.0 Remoting
#
$CitrixServer = 'ctx001'

$session = New-PSSession -ComputerName $CitrixServer
Enter-PSSession -Session $session

Add-PSSnapin Citrix*

In case of MFCOM-based Legacy Farm Server you can either create a PowerShell Remoting Session like above or – if you still use PowerShell 1.0 you have no other choice – create a remote COM object:

#
# Remote MFCOM w/ PowerShell 2.0 Remoting
#
$CitrixServer = 'ctx001'

$session = New-PSSession -ComputerName $CitrixServer[activator]::CreateInstance($type, $null)
Enter-PSSession -Session $session

$farm = New-Object -ComObject 'MetaFrameCOM.MetaFrameFarm'

#
# Remote MFCOM w/ PowerShell 1.0 and .NET
#
$CitrixServer = 'ctx001'

$type = [type]::GetTypeFromProgID('MetaFrameCOM.MetaFrameFarm', $CitrixServer)

$farm = [activator]::CreateInstance($type, $null)

Category:Citrix, PowerShell | Comment (0) | Author: Frank-Peter

How To Migrate A “Legacy” Citrix Farm To XenApp 6?

Wednesday, 9. June 2010 15:01

Recently, I wrote here about the release of the Citrix XenApp 6 PowerShell SDK and that XenApp 6 neither supports nor contains the MFCOM programming interface of XenApp 5 and older versions (which I will call Legacy Farm from now on).

Upcoming Legacy Farm to XenApp 6 migration projects will definitely go along with the requirement to automatically export the Legacy Farm’s objects like Applications or Administrators in order to import them into the new XenApp 6 Farm. Since XenApp6 has no MFCOM support you won’t be able to use MFCOM based migration scripts you may have written for former migration projects. What you need instead is a Migration Tool that “speaks” both MFCOM and XenApp 6 Commands – meaning a tool that is able to communicate with the Legacy Farm as well as the new farm.

Good news is that such a tool is already available, currently it is still in Beta and it is supposed to be released in mid of this month – the Citrix XenApp 6 Migration Tool (Download)

The XenApp 6 Migration Tool contains a PowerShell 2.0 Module whose Cmdlets and Functions can be used to export Legacy Farm settings from a XenApp 5 Farm and import them to a XenApp 6 Farm. The following types of objects can be migrated:

  • Application
  • Folder
  • Load evaluator
  • Policy
  • Server configuration
  • Farm configuration
  • Administrator

Settings you can’t transfer:

  • Zones
  • Printer management
  • Configuration logging settings

The XenApp 6 Migration Tool supports only migration from a single XenApp 5 Farm. It isn’t possible to consolidate multiple Legacy Farms into one XenApp 6 Farm. The Legacy Farm must be based on XenApp 5 for WS2003 + HRP5 or XenApp 5 for WS2008. The new farm must be running XenApp 6 for WS2008 R2.

Category:Citrix, PowerShell | Comment (0) | Author: Frank-Peter

XenApp 6 PowerShell SDK Released

Sunday, 9. May 2010 17:16

About two weeks ago Citrix has released the XenApp 6 PowerShell SDK that enables people to manage XenApp 6 farms using Microsoft PowerShell scripting. The modules included in the SDK are Citrix XenApp Commands (aka XenApp cmdlets), Citrix Group Policy Provider, and Citrix Common Commands. The Citrix Principal Design Engineer Tom M Kludy wrote some nice blogs about the XenApp 6 SDK.

Reading the XenApp cmdlets reference I found the following information for those of you who are familiar with MFCOM:

Starting in XenApp 6.0, MFCOM as a publically supported programming and scripting interface will no longer be available. All existing MFCOM-based code no longer works on XenApp 6.0. No doubt that the absence of MFCOM will be something that requires additional effort to the adoption of XenApp 6.0.

Take a deep breath and don’t panic! Most of MFCOM scripts can be replaced by one-liners meaning that most of the short MFCOM scripts are obsolete as they’ve been replaced by PowerShell cmdlets.

For example adding a Citrix admin with the XenApp 6 PowerShell SDK is a one-liner:

New-XAAdministrator –AdministratorName ctxadmin –AdministratorType Full

In MFCOM, an administrator would need to write a script with dozens lines of code like below to accomplish the same task. So after all, it was definitely a good idea to discontinue MFCOM Support.

function Test-IsCitrixAdministrator(){

	$MetaFrameWinFarmObject = 1

	$MFCOM = New-Object -ComObject 'MetaframeCOM.MetaFrameFarm'
	if (!$MFCOM)
	{
		$result = $false
	}
	else
	{
		$MFCOM.Initialize($MetaFrameWinFarmObject)
		$result = [boolean] $MFCOM.WinFarmObject.IsCitrixAdministrator
		$MFCOM = $null
	}
	$result
}

function Test-ADGroup(){

	param(
		$name = $(throw "No AD group name specified.")
	)

	$domainRoot = [System.DirectoryServices.ActiveDirectory.Forest]::GetCurrentForest().RootDomain.Name
	$searcher = New-Object System.DirectoryServices.DirectorySearcher([ADSI]"GC://$domainRoot")
	$searcher.Filter = "(&(objectClass=Group)(name=$name))"
	$result = $searcher.FindOne()
	[boolean] $result
}

function Add-CitrixAdmins(){

	param(
		$accounts = $(throw "No comma-seperated list of Citrix Admin accounts specified."),
		$adminType = $(throw "No Admin Type specified. (Full or ViewOnly?)")
	)

	$MetaFrameWinFarmObject     = 1
	$MFAdminPermissionViewOnly  = 1
	$MFAdminPermissionFullAdmin = 3
	$MFAccountAuthorityADS      = 3
	$MFAccountGlobalGroup       = 4
	$MFAccountEnable            = 1

	switch($adminType)
	{
		"Full"
		{
			$MFAdminPermission = $MFAdminPermissionFullAdmin
		}

		"ViewOnly"
		{
			$MFAdminPermission = $MFAdminPermissionViewOnly
		}

		default
		{
			throw "This function supports only the Citrix Admin Privilege levels Full and ViewOnly."
		}
	}

	$MFCOM = New-Object -ComObject 'MetaframeCOM.MetaFrameFarm'
	$MFCOM.Initialize($MetaFrameWinFarmObject)

 	$currentAdmins = $MFCOM.Admins | %{$_.FriendlyName}
	$newAdmins     = $accounts.Split(",")

	$defaultDomain = $env:USERDOMAIN

	foreach($account in $newAdmins)
	{
		$newAdmin = $account.Split("\")
		if($newAdmin.Count -eq 1)
		{
			$accountDomain = $defaultDomain
			$accountName = $account
			if (-not (Test-ADGroup $accountName)){throw "Group does not exist in $accountDomain - $accountName"}
		}
		else
		{
			$accountDomain = $newAdmin[0]
			$accountName = $newAdmin[1]
		}

		if($currentAdmins -notcontains "$accountDomain\$accountName")
		{
			$mfAdmin = $MFCOM.AddAdmin()
			$mfAdmin.AdminType = $MFAdminPermission
			$mfAdmin.AAType = $MFAccountAuthorityADS
			$mfAdmin.AAName = $accountDomain
			$mfAdmin.AccountType = $MFAccountGlobalGroup
			$mfAdmin.AccountName = $accountName
			$mfAdmin.Enable = $MFAccountEnable
			$mfAdmin.SaveData()
			$mfAdmin = $null
		}
	}
}

Category:Citrix, PowerShell | Comments (3) | Author: Frank-Peter

Citrix Provisioning Services 5.1’s PowerShell Interface, Part III

Saturday, 19. December 2009 12:40

Hello again and welcome to the third part of my blog post series dealing with McliPSSnapIn – the PowerShell SnapIn to automate the Citrix Provisioning Services (PVS). Hope you enjoyed the first two parts and played with the McliPSSnapIn cmdlets meanwhile. This time you’ll learn how to handle the output of the Mcli-Get cmdlet meaning how to convert its structured text to an object (or an array of objects).

For example, suppose you want to display all vDisks sorted by size in descending order. By intuition, Mcli-Get DiskInfo and Sort-Object seem well-suited for this job get done.

PS C:\> Mcli-Get DiskInfo | Sort-Object diskSize -Descending

Worse luck! This will end up in a mess (which I don’t want to show here). As formerly mentioned, McliPSSnapIn’s cmdlets don’t deliver objects but rather text information just in the fashion of a legacy console utility. This is not much fun.

Since PowerShell supports the creation of custom objects you can convert Mcli-Get’s output to your own “homebrew” object, strictly speaking to a Device, Disk, Site, Store, Farm, whatever PVS object. Look at this working solution:

function PVS\Get-DiskInfo
{
	$record = $null
	$diskInfo = @()
	switch -regex (Mcli-Get DiskInfo)
	{
		"^Record\s#\d+$"
		{
			if ($record)
			{
				$diskInfo += $record
			}
			$record = New-Object System.Object
		}
		"^\s{2}(?<Name>\w+):\s(?<Value>.*)"
		{
			$record | Add-Member -Type NoteProperty -Name $Matches.Name -Value $Matches.Value
		}
	}
	if ($record)
	{
		$diskInfo += $record
	}
	return ,$diskInfo
}

Compared to the task to create an object and to assign property names with values to that object it is actually more challenging to parse the text-based information in order to identify the information related to each single object.

You’re not scared now, are you? ;-) Let’s take one thing at a time – according to Louis X divide et impera strategy.

First, let’s analyze the text returned by Mcli-Get DiskInfo on my test system:

PS C:\> Mcli-Get DiskInfo

Executing: Get DiskInfo
Get succeeded.  3 record(s) returned.
Record #1
  diskLocatorId: 5c3e5124-158b-44e7-891a-f600d1ebd072
  diskLocatorName: VDISK
  ...
  diskSize: 41940702720
  ...
  deviceCount: 0
  locked: 0

Record #2
  ...

Record #3
  ...

PS C:\> 

The cmdlet returned the “properties” of three vDisks. The text is structured nicely in order to support human readability:

  • The output begins with a short summary followed by an information block for each returned record (object).
  • Each information block is introduced with “Record #nnn” where nnn stands for a counter
  • Furthermore empty lines separate the information blocks from each other.
  • The lines that contain the actual data for the records (objects) are indented “name: value” pairs like “diskSize: 41940702720”

Based on this research findings you’re able design an algorithm as stated above to convert the cmdlet’s output to an array of objects.

The Power Of “switch -regex” Unveiled

Naturally you need to process the returned text data in a loop. At first glance it seems perfectly possible to use a foreach loop as follows:

foreach ($line in $(Mcli-Get DiskInfo))
{
	...
}

Inside each run it is important to distinguish between three cases:

  • A new information block begins (initiated by “Record #nnn”)
  • Information for the current record (object) in the form of “name: value
  • Superfluous information (as for instance the summary)

I consider this a classic case for a switch statement with the -regex option in order to identify a match with “Record #nnn” and a match with “name: value”.

foreach ($line in $(Mcli-Get DiskInfo))
{
	switch -regex ($line)
	{
		...
	}
}

You know what? switch is able to process a pipeline (an array of elements) itself, meaning you can forget foreach and let switch process Mcli-Get’s output directly:

switch -regex (Mcli-Get DiskInfo)
{
	...
}

According PowerShell’s help, the switch statement’s -regex option “indicates that the match clause, if a string, is treated as a regex string.” So, you need two regex strings, one to identify a “Record #nnn” line and one to identify a “name: value” pair. I will explain my regex statements below.

This regex string matches “Record #nnn”: ^Record\s#\d+$

Element Description
^ Matches the beginning characters
Record Matches “Record”
\s Matches any single white-space character
# Matches “#”
\d Matches any decimal digit
+ Matches repeating instances of the preceding characters
$ Matches the end characters

 

This regex string matches “name: value”: ^\s{2}(?<Name>\w+):\s(?<Value>.*)

Element Description
^ Matches the beginning characters
\s{2} Matches exactly two white-space characters
(?<Name>\w+) Matches any word character until the next non-word character and assign the result to $Matches.Value
: Matches “:”
\s Matches any single white-space characters
(?<Value>.*) Matches the rest of the line and assign the result to $Matches.Value

 

And what is $Matches? PowerShell places resulting matches automatically in the $Matches hashtable where $Matches[0] always holds the entire match. Furthermore, PowerShell allows to specify (optionally named) capture groups inside a regex string. These captures will be assigned to separate elements of the $Matches hashtable, either numbered or named. The example below shows what I’m trying to explain:

PS C:\> # Unnamed capture groups in a regex string
PS C:\> "  theName: the Value" -match "^\s{2}(\w+):\s(.*)"
True
PS C:\> $Matches

Name                           Value
----                           -----
2                              the Value
1                              theName
0                                theName: the Value

PS C:\> # Named capture groups in a regex string
PS C:\> "  theName: the Value" -match "^\s{2}(?<Name>\w+):\s(?<Value>.*)"
True
PS C:\> $Matches

Name                           Value
----                           -----
Value                          the Value
Name                           theName
0                                theName: the Value

PS C:\> $Matches.Name
theName
PC C:\> 

This is so cool! Did I already mention that I love PowerShell?

How To Avoid Flattening Of Arrays?

I need to tell you that the return value of the above-stated Get-DiskInfo function messed me around a while.

The function should always return an array regardless of the number of objects found by Mcli-Get. The problem I was running into was that the function didn’t returned an array when the object count was 1. Instead it returned the object directly.

In order to avoid that PowerShell unrolls or flattens the array when it would contain only one element I used the comma operator to force the function to return the array as is:

return ,$diskInfo

Ah, I’ve missed to show the Get-DiskInfo function in action:

PS C:\> # Is the function loaded?
PS C:\> test-path function:pvs\get-diskinfo
True
PS C:\> # assign the returning objects to $vdisks
PS C:\> $vdisks = pvs\get-diskinfo
PS C:\> # show disk sizes in gb
PS C:\> $vdisks | %{
>> "Disk Name: " + $_.diskLocatorName
>> "Disk Size: {0:N2}" -f ($_.diskSize/1gb)
>> }
>>
Disk Name: VDISK
Disk Size: 39.06
Disk Name: TestDisk
Disk Size: 10.00
Disk Name: VDISK2
Disk Size: 39.06
PS C:\> 

The Get-DiskInfo function is a very basic example for creating custom objects in PowerShell and therefore should considered to be a kick-start.

I hope it inspired you to dive deeper at Christmas time.

Category:Citrix, PowerShell | Comment (0) | Author: Frank-Peter

Citrix Provisioning Services 5.1’s PowerShell Interface, Part II

Saturday, 5. December 2009 19:55

Welcome to the second part of my series of blog posts dealing with the PowerShell SnapIn called McliPSSnapIn that Citrix provides as an programming interface for the Citrix Provisioning Services (PVS).

The first part only covered the installation/registration of the SnapIn and furthermore it introduced a function called Test-PSSnapIn to add the SnapIn to the current PowerShell session (respectively to test if it could added successfully).

This time I want to point out more clearly how the McliPSSnapIn’s cmdlets vary from PowerShell standard. It’s just that the SnapIn has its own standard after all. A general approach to test if a given PVS object exists (like a vDisk, Device, or Collection) will top off part II.

McliPSSnapIn Standards

As mentioned in the prologue of part I, the cmdlets of McliPSSnapIn are not 100% PowerShell-compliant meaning that…

  • the cmdlet names don’t meet PowerShell’s “Verb-Noun” naming schema,
  • the cmdlet’s syntax doesn’t match with PowerShell’s standards, and
  • the cmdlets don’t return objects but arrays of pure text elements.

Instead, the McliPSSnapIn introduces its own standards – anything is better than nothing.

Cmdlet Naming

Overall there are only nine cmdlets or “command types” (Citrix-terminology):

Mcli-Add, Mcli-Delete, Mcli-Get, Mcli-Help, Mcli-Info, Mcli-Run, Mcli-RunWithReturn, Mcli-Set, and Mcli-SetList

You see, the cmdlet naming doesn’t follow PowerShell’s “Verb-Noun” principle but rather its own standard that is “Mcli-Verb” or “Mcli-Action” (according Citrix-terminology). Ah, did I already mention that Mcli stands for Management Command Line Interface?

Cmdlet Syntax

No worries! I won’t write down a boring command reference now. If you need a reference look at Citrix’ PVS Programmer’s Guide, and there’s the Mcli-Help cmdlet by the way. This section just outlines the things in common concerning the syntax of all Mcli cmdlets.

Mcli-Help left out, the general syntax may be summarized as follows:

Mcli-<Action> <ObjectType> <Arguments>

Basically, in terms of PowerShell standard you can take “Action” for “Verb” and “ObjectType” for “Noun” – not so bad after all.

Actions

Besides “Help” there are eight Actions:

Mcli-Add Add an object and return the object’s unique Id
Mcli-Delete Remove an object
Mcli-Get Get one or more objects and return according information as array of strings
Mcli-Info Get information about objects and return them as array of strings
Mcli-Run Run an action without return value
Mcli-RunWithReturn Run an action and return information as array of strings.
Mcli-Set Set property values for one or more objects without return value
Mcli-SetList Set a list of property values for an object without return value

ObjectTypes

Depending on the action there are more or less object types. To name but the most common only:

Collection, Device, DiskLocator, Server, Site, and Store

But as said before there are many more.

Arguments

There are four types of arguments indicated by “-f”, “-r”, “-p”, and “-s”:

-f Indicates a field (object property name) or a list of fields, separated by comma, to be returned.
-p Indicates a single or a comma-separated list of name=value pair parameters to identify an object.
-r Indicates a single or a comma-separated list of name=value pair records to be set on an object.
-s Indicates a field to sort on.

Syntax Examples

Some examples dealing with the Device object will certainly help to to clarify so much theory:

# Set the className to "Collection Class" in all of the Device objects of the Site
Mcli-Set Device -p collectionName=Collection, siteName=Site -r className="Collection Class"

# Add a Device
Mcli-Add Device -r deviceName=Device1, deviceMac=1a-2b-3c-4d-5e-6f, description="A description"

# And delete the Device
Mcli-Delete Device -p deviceName=Device1

# Get all Devices in the System you have access to
Mcli-Get Device

# Get a list of Devices in the Collection of Site. The only fields that will be returned
# are deviceName and description and the sort order will be deviceName decending.
# It is decending because of the - appended to the -s deviceName
Mcli-Get Device -p collectionName=Collection, siteName=Site -f deviceName,
description -s deviceName-

# Reboot all of the devices in the Collection of the Site
Mcli-Run Reboot -p collectionName=Collection, siteName=Site

# Return the count of Devices in the Collection of the Site
Mcli-Run DeviceCount -p collectionName=Collection, siteName=Site

# Return the count of Devices in the Collection of the Site
mcli-Run DeviceCount -p collectionName=Collection, siteName=Site

These samples are taken from the PVS Programmer’s Guide. I don’t know if they really work but it is good enough to show how the Mcli cmdlet syntax concept differs from PowerShell standards.

Cmdlet Return Values

The real annoyance about the Mcli SnapIn is the fact that their cmdlets return simple text, more precisely: arrays of strings. Practically the return values don’t differ from the return values of an external console utility like ipconfig.exe.

For example, it is well established that PowerShell’s most awesome feature is the Object Pipeline and it is a pity that you can’t use this extremely resourceful feature with the Mcli cmdlets. (Well, you can use the Object Pipeline in order to apply it to the string objects the cmdlets return though.)

Consequently, you need to inspect returning data using string operation methods like Substring() or Trim(). And PowerShell wouldn’t be PowerShell without its great Regular Expression support that helps tokenize strings to identify requested data (which is an easy task once you have played with regular expressions and understand how they’re working).

If you’re willing to put more effort in development time you can write your own PowerShell function library to encapsulate the Mcli cmdlet’s functionality completely and create your “homebrew” Collection, Device, Store, and Site objects with scripted methods. Maybe – it depends on your comments, folks! – I will provide some examples how to create your own object in a future part of this series about the PVS’ PowerShell SnapIn.

Hey, wake up! We’re back in example mode. ;-) The cmdlet Mcli-Add returns a unique Id of the object added. Look at this example output:

PS C:\> $rv = Mcli-Add Device -r siteName=Site, collectionName=Collection, deviceName=Device1, deviceMac=1a-2b-3c-4d-5e-6f
PS C:\> $rv

Executing: Add Device
Add succeeded.  resultId = 27ce63d7-c469-42ed-b828-82bad1d3c616
PS C:\> 

So, besides the interesting data (meaning the Add succeeded and the resultId) there’s virtually gross information. Now, let’s inspect the return value stored in the $rv variable….

PS C:\> $rv.Count
3
PS C:\> $rv[0]

PS C:\> $rv[1]
Executing: Add Device
PS C:\> $rv[2]
Add succeeded.  resultId = 27cd63d7-c469-24ed-b828-82bad1d3c616
PS C:\> 

You see, Mcli-Add didn’t return a Device object with all its properties and methods. Actually, it returned an array of three strings (the first is empty). Therefore, if you want to process the new Device – let’s say you need to set a property value (Citrix-terminology: record value) – you can’t just do it the way you might expect by typing this:

PS C:\> # Does not work!
PS C:\> $rv.description = "A description"
Property 'description' cannot be found on this object; make sure it exists and is settable.
At line:1 char:5
+ $rv.d <<<< escription = "A description"
PS C:\> 

No, instead you need to fire up another cmdlet, Mcli-Set, to apply the desired property value:

PS C:\> Mcli-Set Device -p deviceName=Device1 -r description="A description"

Executing: Set Device
Set succeeded.
PS C:\> 

So far, so good. The cmdlets work, but hopefully you’re getting the picture now why I consider the concept how Mcli works annoying. It’s not that ingenious PowerShell way, it’s just old school (meaning text based) scripting.

Test Object Existence

In this chapter I want to share a set of functions that test for existence of a given object, let’s say a Device.

For example the function PVS\Test-Device returns either “True” or “False” depending on the text information returned by Mcli-RunWithReturn. Let’s take a look at the function first:

function PVS\Test-Device ($Name)
{
	(Mcli-RunWithReturn Exists -p deviceName=$Name)[2] -match 1
}

Actually, the function returns “True” or “False” based on a regular expression comparison applied to the third line of Mcli-RunWithReturn’s text output. What matters is that the text of the third line matches the character “1”, meaning if there’s no “1” the given Device name doesn’t exist. Let’s have a look at the Mcli-RunWithReturn’s complete output:

PS C:\> #Test with an existing Device name
PS C:\> Mcli-RunWithReturn Exists -p deviceName=Device1

Executing: RunWithReturn Exists
RunWithReturn succeeded.  returnValue = 1
PS C:\> #Test with an non-existin Device name
PS C:\> Mcli-RunWithReturn Exists -p deviceName=Device2

Executing: RunWithReturn Exists
RunWithReturn succeeded.  returnValue = 0
PS C:\> 

You see, Mcli-RunWithReturn’s output differs only at the position where the cmdlet displays the return value. To prove that the function is working:

PS C:\> function PVS\Test-Device ($Name) {
>> (Mcli-RunWithReturn Exists -p deviceName=$Name)[2] -match 1
>> }
>>
PS C:\> #Test with an existing Device name
PS C:\> pvs\test-device Device1
True
PS C:\> #Test with a non-existing Device name
PS C:\> pvs\test-device Device2
False
PS C:\> 

Taking the PVS\Test-Device function as a reference it is easy to build more functions to test for example the existence of a Site, Store, or Disk:

function PVS\Test-Site ($Name)
{
	(Mcli-RunWithReturn Exists -p siteName=$Name)[2] -match 1
}

function PVS\Test-Store ($Name)
{
	(Mcli-RunWithReturn Exists -p storeName=$Name)[2] -match 1
}

function PVS\Test-Disk ($Name, $SiteName, $StoreName)
{
	(Mcli-RunWithReturn Exists -p diskLocatorName=$Name, siteName=$SiteName, storeName=$StoreName)[2] -match 1
}

The PVS\Test-Disk function needs some more parameters due to the fact that Mcli-RunWithReturn needs also the name of the Site and the Store to determine if a given Device name there exists.

You’ve reached the end of part II. Next part I will dive deeper into scripting tasks finally. So stay tuned and don’t miss the chance to write comments meanwhile…

Category:Citrix, PowerShell | Comment (0) | Author: Frank-Peter

Citrix Provisioning Services 5.1’s PowerShell Interface, Part I

Wednesday, 2. December 2009 21:02

With this post, I want to share some functions to automate Citrix Provisioning Services (PVS) 5.1. But let me tell a short story first…

Prologue

One of these days, I had to implement some PVS functionality into Solution4 at a customer. Only knowing by hearsay that a PowerShell programming interface or PowerShell SnapIn to automate PVS 5.1 exists, first I considered that task pretty easy. Some days later, during getting prepared for the job, the initial thrill of anticipation steadily changed into a feeling of disillusion. Why? I will tell you why.

To put it mildly, the PowerShell SnapIn for PVS 5.1 – baptized McliPSSnapIn – takes some getting used to, meaning it doesn’t follow any common PowerShell standards like the naming convention or the “syntax design” of cmdlets (to name the least worse first). The data returned from McliPSSnapIn’s cmdlets is – simple text! For example you won’t get an array of vDisk objects from Mcli-Get DiskLocator as one might expect with reason. Instead Mcli-Get returns an array of pure text lines without any deeper relation to the information. Thus you need to parse returning data just the way if they would coming from an external console utility – Whoop! Welcome to Split(), Substring(), Trim(), and their friends, not forgetting Regular Expressions that can make your day if you know how to use them.

Since McliPSSnapIn’s cmdlets are working I won’t claim that this SnapIn is load of crap. Nevertheless it’s annoying and I hope that Citrix will provide a PowerShell-compliant interface for PVS better late than sorry. (Yes, They Can! For example Citrix yet has done a good job on developing a PowerShell programming interface to automate Citrix XenApp – the Citrix XenApp Commands.)

First of all, you should get a copy of the Citrix Provisioning Services 5.1, 5.1 SP1 PowerShell Programmer’s Guide that is published in the Citrix Knowledge Center Article CTX121334. Don’t expect too much from this PDF document, it contains 150+ pages information though. The guide mainly consists of highly purified syntax help and much repeated information but it lacks substance like detailed descriptions. It is worth to use as command reference anyways.

Enough talk. (Did I already mention that I don’t like to talk?) Let’s have a closer look at McliPSSnapIn now.

Preparation

I believe you’re reading this article because you have Citrix Provisioning Services 5.1 (SP1) up and running and now need to realize some automation tasks in Windows PowerShell. Already mentioned previously, the essential requirement to automate Citrix PVS with PowerShell is the McliPSSnapIn PowerShell SnapIn.

McliPSSnapIn is a dll file and part of the Provisioning Services Console. Unsurprisingly you find the file in the PVS Console’s installation directory that is normally %ProgramFiles%\Citrix\Provisioning Services Console.

Before you can add the SnapIn to a PowerShell session with Add-PSSnapIn it needs first to be registered in PowerShell. This is a one-time task. As stated correctly in the PVS Programmer’s Guide, this can be done by running the .NET Framework’s utility InstallUtil.exe – see page 7, I don’t want to crib ;-)

How can I find out if the McliPSSnapIn is added to the current PowerShell session?

Several ways exist to check if a PowerShell SnapIn is added to the current PowerShell session. My way is a tiny little bit brute force since it simply tries to add the SnapIn without any previous steps:

function PVS\Test-PSSnapIn ($name = 'McliPSSnapIn')
{
	Add-PSSnapin -Name $name -ErrorAction SilentlyContinue
	[boolean] (Get-PSSnapin | Where-Object {$_.Name -eq $name})
}

First, my function tries to add the SnapIn while suppressing possible error messages by setting the according ErrorAction parameter value (SilentlyContinue). Indeed an error may occur due to the fact that either the SnapIn was added already or the SnapIn is not present on the system. Secondly the function tries to identify the given PSSnapIn using the Get-PSSnapIn cmdlet.

If the SnapIn is added (either just now or previously) the function will return “True”, apart from that it returns “False” if the SnapIn is not installed or registered. Beyond that (in case of return value “False”) you can dredge up the suppressed error message using the standard PowerShell variable $Error as follows:

if (PVS\Test-PSSnapIn)
{
	Write-Host "The SnapIn is loaded"
}
else
{
	Write-Host "The SnapIn is not loaded."
	Write-Host $Error[0].Exception
}

The $Error variable is PowerShell’s error stack. It saves the details of the errors that occurred recently (I don’t know how many). Each error is an array element, and the most recent error details can be found in the first element of the array, that is $Error[0]. The Exception property of the error object provides the error message.

Ok, this is end of the first part. I need to go to bed now ;-) More parts will follow soon. Good night and don’t miss the chance to write comments meanwhile…

Category:Citrix, PowerShell | Comment (0) | Author: Frank-Peter

Assign Citrix policies to servers

Monday, 16. March 2009 14:45

In our scripting framework basically every server takes care of it own configuration. This is also the case when managing Citrix policies. The policies themselves are imported and exported for DTAP and disaster recovery purposes but the server assignment is done by each server itself according to the role of that particular server.

[...]

Category:Citrix, PowerShell | Comments (2) | Author: Dennis Damen