PowerShell: Documenting Citrix Policies

UPDATE: I had updated this script (updated link in the comments below), but had not updated the post with the new script. This has been done.

Disclaimer: This post references PowerShell XenApp Commands that were released as a technology preview for XenApp 4.5/5.0. I have not tested the script against the XenApp 6 which uses a different version of these XenApp Commands.

Documentation is a vital (yet rarely loved) part of systems administration/engineering.  Exhaustively documenting Citrix can turn your hair white.  There are  farm-wide settings, polices, application publishing properties, zone configuration, and that’s just the beginning.

We are in the midst of architecting a new Citrix environment where I work and we are taking advantage of this to review everything we have done before and changing it if needed.  We’re also attempting to document every facet of our new environment which includes, Provisioning (DHCP), XenApp, XenServer, XenDesktop, AppSense, Netscalers, Web Interface, and MS App-V.

For this post, I’m providing a PowerShell script I modified from Kent Finkle that will capture a Citrix Policy and what it’s applied to in a Word document.  The script will also export the Citrix policy and its filter (what it’s applied to) to 2 XML files that can be used to recreate or restore the configuration if it’s lost.
NOTE: This script assumes that the XenApp PowerShell Commands are installed on the server you’re running the script from.  You can download them from

Here’s the script:

# NAME: get-citrixpolicy.ps1
# AUTHOR: Alain Assaf
# DATE  : 5/11/2010
# SOURCE1: Author: Kent Finkle http://kentfinkle.com/CreateSaveWordDoc.aspx
# SOURCE2: Author: Mark Alexander Bain http://command-line-programming.suite101.com/article.cfm/how_to_create_a_word_document_with_powershell
# COMMENT: Output a Citrix Policy to a Word document.
#          Assumes XenApp Commands are installed on source server
# VERSION: 1.0.0 - Initial script
# VERSION: 1.0.5 - Added policy filter and xml export of policy and filter
# VERSION: 1.0.6 - 8/23/2010 - Added prompt for policy and got document to
#                  automatically save and close.
#Load XenApp Commands
Add-PSSnapin -Name *citrix*

#Initalize farm
$farm = get-xafarm

#Output list of Current Policies and prompt for one to create a document and backup
Get-XAPolicyConfiguration | select PolicyName
$polname = Read-Host "Enter a Citrix Policy to create a report and backup"

#Set variables
$docpath = "\\NETWORKSHARE\Documentation\Architecture\Citrix\policies"
$hname = $env:computername
$uname = $env:username
$a = get-date –format g
$b = get-date -uformat "%m%d%Y"
$oMissing = [System.Reflection.Missing]::Value

#Test path & create if not present
if (!(Test-path -path $docpath)) { new-item $docpath -type directory | out-null }

# Create new Word document
$objWord = New-Object -comobject Word.Application
$objWord.Visible = $True

$objDoc = $objWord.Documents.Add($oMissing, $oMissing, $oMissing, $oMissing)
$objSelection = $objWord.Selection

$objSelection.Font.Name = "Arial"
$objSelection.Font.Size = "18"
$objSelection.TypeText("Citrix Policy Report")
$objSelection.TypeText(" for Farm: " + $farm.FarmName)
$objSelection.Font.Size = "8"
$objSelection.Font.Italic = $True
$objSelection.TypeText("Script run on: " + $hname)
$objSelection.TypeText(" by: " + $uname)
$objSelection.TypeText(" at " + $a)
$objSelection.Font.Italic = $False

$objSelection.Font.Size = "10"
$policy = Get-XAPolicyConfiguration -PolicyName $polname
$objSelection.Font.Bold = $True
$objSelection.TypeText("Citrix Policy: " + $policy.PolicyName)
$objSelection.Font.Bold = $False
$objSelection.Font.Size = "10"

$outpol = $policy | Out-String
$objSelection.TypeText("" + $outpol)

$policyfilter = Get-XAPolicyFilter -PolicyName $polname
$objSelection.Font.Bold = $True
$objSelection.TypeText($policyfilter.PolicyName + " policy applied to:")
$objSelection.Font.Bold = $False
$objSelection.Font.Size = "10"

$outpolfilter = $policyfilter | Out-String
$objSelection.TypeText("" + $outpolfilter)

$doctitle = $farm.FarmName + "_" + $policy.PolicyName + "_" + $b
$savepath = "$docpath\$doctitle.doc"

$poltitle = $farm.FarmName + "_" + $policy.PolicyName + "_Policy" + "_" + $b
$polfiltertitple = $farm.FarmName + "_" + $policyfilter.PolicyName + "_PolicyFilter" + "_" + $b

export-clixml -path "$docpath\$poltitle.xml" -InputObject $policy
export-clixml -path "$docpath\$polfiltertitple.xml" -InputObject $policyfilter

Here’s a sample of the Word document (sanitized for public consumption):


The intention is to run this script periodically to provide documentation and a backup of all the policies applied to a farm.  I encourage you to explore the PowerShell commands provided by Citrix.  You will be able to document every aspect of your farm and also have an easy way to backup/restore the information as needed.



EdgeSight: Trending User Login Detail

My co-worker John Smith has started a second blog titled: EdgeSight Under the hood.  He delves into the vast amount of data that EdgeSight collects and uses SQL queries to present this information in meaningful ways. Inspired by him, I’m going to write about averaging user login metrics that EdgeSight collects, but only reports them on a per user basis.

EdgeSight Views

The EdgeSight database schema is nightmarish, but Citrix collects most of the data in database views (which are basically permanent queries).  The particular view we’re going to look at is the vw_ctrx_archive_server_start_perf view.  Here’s a breakdown of the columns and what they represent:

View Table: vw_ctrx_archive_server_start_perf

Startup detail Server Session Startup Column
Credentials Authentication credentials_authentication_server_duration
Credentials Obtention credentials_obtention_server_duration
Device Mapping device_mapping_server_duration
Login Script Execution login_script_execution_server_duration
Profile Load profile_load_server_duration
Session Creation session_creation_server_duration
Printer creation printer_creation_server_duration
Session Startup Duration Session_startup_server



Users are reporting that it’s taking longer to log into your Citrix farm than a week or two ago.  Suspecting that your AD team has been messing around with the domain login script again you want to see if there’s been a measurable change in how long it takes to process the login script.

Here’s the query:

declare @today datetime
set @today = convert(varchar,getdate(),111)
select convert(varchar(10),dateadd(hh,-4,time_stamp), 111) as [Date], convert(decimal(19,2),avg(login_script_execution_server_duration)/1000.0) as 'Login Script (sec)',  convert(decimal(19,2),avg(Session_startup_server)/1000.0) as 'Session StartUp Total (sec)'
from vw_ctrx_archive_server_start_perf
where convert(varchar(10),dateadd(hh,-4,time_stamp), 111) > @today-30
group by convert(varchar(10),dateadd(hh,-4,time_stamp), 111)
order by convert(varchar(10),dateadd(hh,-4,time_stamp), 111)

This takes the average for all logins (captured by EdgeSight) and averages the login script processing time and the total session startup time.

NOTE: In the dateadd functions you’ll notice a (-4).  This is necessary to offset the data stored in EdgeSight with your time zone.  EdgeSight stores its data based on UTC time.  Using the -4 will move it to Eastern (U.S.) daylight savings time (normally it’s UTC -5).  Keep this in mind if you do ad hoc queries with EdgeSight data.

EdgeSight will, by default, only store this type of information for 30 days.  If you want to report on longer periods, you’ll have to save the information in another form or change the EdgeSight worker to not purge data as often.


Wisdom-Fu: E-mail alert when you find a memory dump

Res software is probably best know for their PowerFuse product which provides powerful and granular control of a system and a user’s environment.  They also have a terrific product called Wisdom which they describe as “Run Book  Automation for Windows.”  We utilize Wisdom every day to manage our XenApp farm and related servers.  I could spend pages and pages gushing about Wisdom, but I’m going to use this post to show how I use Wisdom to accomplish certain tasks. Naturally, the solution I present can be accomplished in a variety of ways, but I find Wisdom to be elegant and have a very short learning curve.  On top of that, it provides extensive,detailed change management and reporting which many products to not.


Typically, a XenApp environment has many, many variables at work that can compromise the stability of a system.  At times, this results in a crash dump and reviewing these dumps can give insight to what caused the problem.

NOTE: If you have a affinity for punishing yourself and want to actually dive into dumps, I highly recommend Crash Dump Analysis by Dmitry Vostokov.  Check out his minidump analysis series to get started.

In our environment, we have many servers and occasionally one will crash, reboot, and come back into production before we get an e-mail alert and we would not know if a dump was generated unless we connected to the server to find it.  I will describe how I created a Wisdom module to detect dump files on a server, copy them to a central location, and send an e-mail alert to the team.

Step 1 – Determine if a dump file exists

You can set up your server to create full and mini dumps by going into Computer properties, clicking the Advanced tab, and selecting Startup and Recovery.


This window will show you where the dump files are being created. Full memory dumps are written to: %SystemRoot%\MEMORY.DMP and minidumps are written to: %SystemRoot%\Minidump\

Here is the Wisdom Module that we’ll dive into:


The Execute Command task:


The command line is: if not exist %WINDIR%\Minidump\*.dmp EXIT /B 1

This is a conditional statement that looks for any file with a *.DMP extension in the Minidump directory.  If the file exists, then the command will successfully end with an error code of 0 (as set by Wisdom) otherwise it will fail and exit with an error code of 1.

Step 2 – E-mail someone that a memory dump file was found

The send e-mail task


I’m highlighting the Condition portion of this task because this is where the conditional logic of the previous task (and its exit codes comes into play).  The condition is whether the previous task was successfully completed.  If so, the we set a DUMPEXISTS parameter to 1 if true or 0 if false.

Step 3 – Copy the memory dump to a network share and off the server

The perform file operations task


The task creates a directory on a share (based on the server name – more on this later), copies the memory dump file to that location and then deletes it from the server.  The condition on this task is the value of DUMPEXISTS, which we set in the previous task.  If the e-mail task ran, then DUMPEXISTS is set to one, so this task will run and move the dump to a network share.

The remaining 3 tasks for this module repeat the previous 3 for the other memory dump type.

Wrapping Up

The SERVERNAME parameter (which is used when we copy the memory dump to the network) is simply formed by the %COMPUTERNAME% variable.  Wisdom, luckily, has access to the environment variables that are set on the machine the task is run on.


Finally, you should set this module to run on every reboot, then you’ll get e-mail alerts that memory dumps were generated if a server crashes.

Building Block

Wisdom allows you to export any resource, module, project or run book as an XML file that can be imorted to another Wisdom database.  Another wonderful feature.  I’ve sanitized a building block of this module for the community.  Due to WordPress file extension restrictions, I’ve renamed the building block with a .DOC extension.  Change it to .XML and you should be able to import it.

module_get dmp files if they exist


A hard, rough, abrasive look at camel spotting…no dromedaries…no virtualization.

%d bloggers like this: