PowerShell SDK versioning strategy

XenDesktop aims to keep its PowerShell SDKs backward compatible, which means that admins (via the command interface or scripts) do not need to be aware of which version of the Controller services are being communicated with, or which version of the client modules/snap-ins are installed.

However from time to time there are changes that are significant enough to make this not possible.   The aim is still to keep as many of the cmdlets backward compatible as possible so scripts continue to operate, however it might be that a new PowerShell client side module/snap-in is required to for new service versions.  This is the case for the XD 7 release where new PowerShell snap-ins are provided that will not operate with XD 5.x services and you cannot use XD5.x snap-ins to communicate with XD7 services.

The other aspect to versioning is that as new versions of XenDesktop are released, new functionality is added and scripts/admins need to be able to utilise these where possible.

Writing scripts to handle different versions

There are different techniques that you might want to deploy depending on what you are trying to achieve, and if the scripts need to dynamically change or if they are just detecting version up front then running the appropriate set of cmdlets.  The section below defines some techniques that can be used.

Loading SDK client

All SDK modules/snapins have unique names of the format Citrix.<module>.Admin.V<version number>.  This enables multiple versions of the same SDK to be installed and even loaded at the same time if required.

The required version of a snapin can be determined by looking at the SDK service version  reported by the Central Configuration Service records for the site (i.e. Get-ConfigRegisteredServiceInstance -InstanceType SDK).

The correct module/snap-in can then be loaded or if all modules/snap-ins for the service are loaded the correct one can be used by name-spacing it (to avoid conflicts) e.g. Citrix.ADIdenity.Admin.V2\Get-AcctIdentyPool would use the V2 snap in.

Note: The Central Configuration Service snapin is special in that V2 of the snap-in has limited ability to communicate with both the V1 and V2 Central Configuration Services; The main functionality which is available  for a V2 snap-in talking to a V1 service is the Get-RegisteredServiceInstance cmdlet.

Feature availability

Features can be present in the Controller services but not available from the client when the client is of an older version than the services, in addition they can be unavailable even though the cmdlets for them are present on the client if the service on the controller is older than the client version.  It is therefore not really a good idea to assume that just because there is a cmdlet with the parameter you want to use available that it will work as expected.

The way XenDestkop SDKs provide for dealing with this is via a capability mechanism.  Each PowerShell module/snap-in has a cmdlet called Get-<prefix>ServiceAddedCapability.  This cmdlet will return a list of supported features for the combination of service and client that is being used.

This enables choices to be made based on the capabilities that are defined as available.

Example Script

Below is an example script that demonstrates these techniques.  This is not meant to define a definitive pattern for solving these problems but instead provide examples of ways in which they could be used if desired.

#load an appropriate Central Configuration Service snap-in
$CCS_snapin_toUse = ""
$CCS_loaded_snapins = @(Get-PsSnapin Citrix.Configuration.Admin.*)
if ($CCS_loaded_snapins.Count -eq 0){
 #no CCS snapins for CCS loaded so load one
 $CCS_Available_snapins = @(Get-PsSnapin Citrix.Configuration.Admin.* -reg)
 add-PSSnapin -name $CCS_Available_snapins[0].Name
}
if ($CCS_loaded_snapins.Count -eq 1){
 #a CCS snapin is loaded use it
 $CCS_snapin_toUse = $CCS_Available_snapins[0]
}
if ($CCS_loaded_snapins.Count -gt 1){
 #more than one CCS snapin is loaded use the latest one first
 $CCS_snapin_toUse = $CCS_Available_snapins[($CCS_Available_snapins.Count-1)]
}

#need to namespace this call incase there is more than one loaded here
$CLServiceSDKExpression = "$(($CCS_snapin_toUse).Name)\Get-ConfigRegisteredServiceInstance -InterfaceType SDK -ServiceType Log"
$CLServiceSDK = Invoke-Expression $CLServiceSDKExpression
$BrokerServiceSDKExpression = "$(($CCS_snapin_toUse).Name)\Get-ConfigRegisteredServiceInstance -InterfaceType SDK -ServiceType Broker"
$BrokerServiceSDK = Invoke-Expression $BrokerServiceSDKExpression
# could carry on with all Citrix snap-ins loaded and then name-space every cmdlet call
# ------------------ or ----------------------
# just make sure that the versions needed are loaded for the environment that you are talking to
# this script removes all snap-ins then re-adds the required versions to avoid name-spacing issues
# Another alternative at this point is to branch off separate process based on version detected that
# was exclusively for that version

#make sure that there are no snap-ins already loaded for the services needed
Remove-PsSnapin Citrix.ConfigurationLogging.Admin.*
Remove-PsSnapin Citrix.Broker.Admin.*
#load the Configuration logging snap-in (and use it if one exists... this is new to XD7 so might not exist)
$ConfigLoggingSnapinName = "Citrix.ConfigurationLogging.Admin.V"+$CLServiceSDK.Version
$CLSnapin = Get-PsSnapin $ConfigLoggingSnapinName -Registered -ErrorAction Continue
if ($CLSnapin -ne ""){
 Add-PsSnapin -Name $ConfigLoggingSnapinName
 $highLevelLog = Start-LogHighLevelOperation -Source "testScript" -Text "Demo Operation"
}
#load the right version of the Broker snap-in and if it supports configuration logging use that feature
$BrokerSnapinName = "Citrix.Broker.admin.V"+$BrokerServiceSDK.Version
$BrokerSnapin = Get-PsSnapin $BrokerSnapinName -Registered -ErrorAction Continue
if ($BrokerSnapin -ne ""){
 Add-PsSnapin $BrokerSnapinName
 $catalogs = Get-BrokerCatalog
 # check that the version of the broker available supports configuration logging
 # and respond as required
 if (($highLevelLog -ne "")-and ((Get-BrokerServiceAddedCapability) -contains "ConfigurationLogging")){
 $catalogs | Set-BrokerCatalogMetadata -Name "test" -Value "updated by script" -LoggingId $highLevlLogID.ID
 }
 else{
 $catalogs | Set-BrokerCatalogMetadata -Name "test" -Value "updated by script"
 }
}
if ($highLevlLog -ne ""){
 Stop-LogHighLevelOperation -HighLevelOperationId $highLevelLog.ID -IsSuccessful $True
}