Documenting a Citrix XenApp 6.5 Farm with Microsoft PowerShell and Word – Version 3

The script to document a Citrix XenApp 6.5 farm has proven to be very popular. Recently the script was updated with some significant changes and bug fixes. I have always wanted to take the time to create a version of the script that would output to a Microsoft Word document. Ryan Revord had taken the XenApp 6.0 version of the script and changed it to create a basic Microsoft Word document. Ryan saved me a lot of work but I wanted improve on the document created by adding a cover page, Table of Contents and footer. The basic functionality of the script has not changed since the Version 2 update. This article will explain the changes to the script to create a Word document.

NOTE: This script is continually updated.  You can always find the most current version by going to http://carlwebster.com/where-to-get-copies-of-the-documentation-scripts/

Note: If you do not want to use this version of the script to create a Word document, there is functionally no difference with Version 2. Version 2 outputs to a formatted text file and this version creates a Word file with headers and formatting. This script has been tested with Word 2007, 2010 and 2013.

As much as Microsoft pushes PowerShell for their products and on their users, I find it surprising there is no native PowerShell support for any of the Office products. For example there is no New_WordDocument, Get-WordDocument or Save-WordDocument. In order to use Word with PowerShell you must use the Word COM Object model. Finding detailed information on that subject was not easy. Fortunately for me, Jeff Hicks had blogged about a presentation he did in 2012 where he linked to some sample PowerShell script files. One of his sample files gave me the start I needed.

The prerequisites to follow along with this article are:

  • A server, physical or virtual, running Microsoft Windows Server 2008 R2 with or without SP1
  • Citrix XenApp 6.5 installed with or without HRP01
  • Word 2007, 2010 or 2013 installed on the computer running the script

Let’s start at the beginning of the script. There are three pieces of information the script needs for the Cover Page and Footer:

  1. Company Name
  2. Cover Page
  3. User Name

Since a digitally signed version of the script is provided, these three pieces of information need to be passed to the script as parameters. A signed PowerShell script cannot be modified or the script is rendered useless.

[CmdletBinding( SupportsShouldProcess = $False, ConfirmImpact = "None", DefaultParameterSetName = "" ) ]

Param( [parameter(
 Position = 0,
 Mandatory=$false )
 ]
 [Alias("CN")]
 [ValidateNotNullOrEmpty()]
 [string]$CompanyName=(Get-ItemProperty -Path "HKCU:\Software\Microsoft\Office\Common\UserInfo" | select -ExpandProperty Company),

 [parameter(
 Position = 1,
 Mandatory=$false )
 ]
 [Alias("CP")]
 [ValidateNotNullOrEmpty()]
 [string]$CoverPage="Motion",

 [parameter(
 Position = 2,
 Mandatory=$false )
 ]
 [Alias("UN")]
 [ValidateNotNullOrEmpty()]
 [string]$UserName=$env:username )

These parameters give us $CompanyName, $CoverPage and $UserName. Each has an alias: CN for CompanyName, CP for CoverPage and UN for UserName.

The default for $CompanyName is read from the registry key where Microsoft Office stores user information.

The default for $CoverPage is Motion. Motion is a Word Cover Page that comes with all three versions of Word, looks good but will require a minor tweak of changing the font used by the date field to a smaller font size.

The default for $UserName is taken from the USERNAME environment variable.

The parameter names can be spelled out or the aliases can be used or any combination. All three of these examples are valid:

-CompanyName “XYC, Inc.” –CoverPage “Grid” –UserName “Joe User”
-CN “XYC, Inc.” –CP “Grid” –UN “Joe User”
-CoverPage “Grid” –UN “Joe User”

For the third example, the default Company Name will be used.

Citrix added PowerShell remoting support to the XenApp 6.5 cmdlets in the fall of 2011. By issuing the Set-XADefaultComputerName cmdlet, you can set the XenApp collector mode server to run the script against. Unfortunately, the Citrix.GroupPolicy.Commands.psm1 module did not have remoting capability added to it. The script needs to determine if remoting is set so the policy processing section of the script can be bypassed.

$Remoting = $False
$tmp = Get-XADefaultComputerName
If(![String]::IsNullOrEmpty( $tmp ))
{
	$Remoting = $True
}

If($Remoting)
{
	write-verbose "Remoting is enabled to XenApp server $tmp"
}
Else
{
	write-verbose "Remoting is not being used"
}

For the Word document to be saved a file name is needed and for the Cover Page, a title is needed.

The filename for the document is the XenApp farm name with the extension DOCX. The document is stored in the folder where the script is run.

# Get farm information
write-verbose "Getting Farm data"
$farm = Get-XAFarm -EA 0

If( $? )
{
	#first check to make sure this is a XenApp 6.5 farm
	If($Farm.ServerVersion.ToString().SubString(0,3) -eq "6.5")
	{
		#this is a XenApp 6.5 farm, script can proceed
	}
	Else
	{
		#this is not a XenApp 6.5 farm, script cannot proceed
		write-warning "This script is designed for XenApp 6.5 and should not be run on previous versions of XenApp"
		Return 1
	}
	$FarmName = $farm.FarmName
	$Title="Inventory Report for the $($FarmName) Farm"
	$filename="$($pwd.path)\$($farm.FarmName).docx"
}
Else
{
	$FarmName = "Unable to retrieve"
	$Title="XenApp 6.5 Farm Inventory Report"
	$filename="$($pwd.path)\XenApp 6.5 Farm Inventory.docx"
	write-warning "Farm information could not be retrieved"
}
$farm = $null

Next, variables specific to Word need to be established. I found two websites that listed the values.

write-verbose "Setting up Word"
#these values were attained from
#http://groovy.codehaus.org/modules/scriptom/1.6.0/scriptom-office-2K3-tlb/apidocs/
#http://msdn.microsoft.com/en-us/library/office/aa211923(v=office.11).aspx
$wdSeekPrimaryFooter = 4
$wdAlignPageNumberRight = 2
$wdStory = 6
$wdMove = 0
$wdSeekMainDocument = 0
$wdColorGray15 = 14277081

Now a Word comObject needs to be created.

$Word = New-Object -comobject "Word.Application"

I will let The Scripting Guy explain the next line.

“We now use the -as operator to turn the string “Microsoft.Office.Interop.Word.WdSaveFormat” into a type. We use [ref] so that we can pass this type by reference, which is a requirement of the Word saveas method. By creating the WdSaveFormat type we gain access WdSaveFormat enumerations, which makes our script easier to write, easier to maintain, and easier to read. This line of code is seen here:”

[ref]$SaveFormat = "microsoft.office.interop.word.WdSaveFormat" -as [type]

Each version of Word comes with Cover Pages and only two are shared across all three versions. The version of Word installed on the computer running the script needs to be determined. If a wrong cover page is passed as $CoverPage and that Cover Page is not in the installed version of Word, the script will run but a lot of errors will be returned. The script needs to validate the $CoverPage against the valid Cover Pages specific to each version of Word.

$WordVersion = [int] $Word.Version
If( $WordVersion -eq 15)
{
	write-verbose "Running Microsoft Word 2013"
	$WordProduct = "Word 2013"
}
Elseif ( $WordVersion -eq 14)
{
	write-verbose "Running Microsoft Word 2010"
	$WordProduct = "Word 2010"
}
Elseif ( $WordVersion -eq 12)
{
	write-verbose "Running Microsoft Word 2007"
	$WordProduct = "Word 2007"
}
Elseif ( $WordVersion -eq 11)
{
	write-verbose "Running Microsoft Word 2003"
	Write-error "This script does not work with Word 2003. Script will end."
	$word.quit()
	exit
}
Else
{
	Write-error "You are running an untested or unsupported version of Microsoft Word.  Script will end."
	$word.quit()
	exit
}

write-verbose "Validate cover page"
$ValidCP = ValidateCoverPage $WordVersion $CoverPage
If(!$ValidCP)
{
	write-error "For $WordProduct, $CoverPage is not a valid Cover Page option.  Script cannot continue."
	$Word.Quit()
	exit
}

The function to validate the Cover Page.

Function ValidateCoverPage
{
	Param( [int]$xWordVersion, [string]$xCP )

	$xArray = ""
	If( $xWordVersion -eq 15)
	{
		#word 2013
		$xArray = ("Austin", "Banded", "Facet", "Filigree", "Grid", "Integral", "Ion (Dark)", "Ion (Light)", "Motion", "Retrospect", "Semaphore", "Sideline", "Slice (Dark)", "Slice (Light)", "ViewMaster", "Whisp")
	}
	ElseIf( $xWordVersion -eq 14)
	{
		#word 2010
		$xArray = ("Alphabet", "Annual", "Austere", "Austin", "Conservative", "Contrast", "Cubicles", "Exposure", "Grid", "Mod", "Motion", "Newsprint", "Perspective", "Pinstripes", "Puzzle", "Sideline", "Stacks", "Tiles", "Transcend")
	}
	ElseIf( $xWordVersion -eq 12)
	{
		#word 2007
		$xArray = ("Alphabet", "Annual", "Austere", "Conservative", "Contrast", "Cubicles", "Exposure", "Mod", "Motion", "Pinstripes", "Puzzle", "Sideline", "Stacks", "Tiles", "Transcend" )
	}

	If ($xArray -contains $xCP)
	{
		Return $True
	}
	Else
	{
		Return $False
	}
}

If an invalid Cover Page is used, the script gives an error, closes Word and exits.

C:\webster\XA65_Inventory_V3.ps1 : For Word 2013, ABC is not a valid Cover Page option.  Script cannot continue.
At line:1 char:24
+ .\xa65_inventory_v3.ps1 <<<<  -CompanyName "The Accidental Citrix Admin" -CoverPage "ABC"
-UserName "Amalgamated Consulting Group" -verbose
+ CategoryInfo          : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId :
Microsoft.PowerShell.Commands.WriteErrorException,XA65_Inventory_V3.ps1
PS C:\webster>

Word is now hidden so the script runs faster by not having to continually update the screen. Change this to $True to see the Word document as it is built.

$Word.Visible = $False

In trying to find resources to figure out how to use PowerShell to create a complex Word document, I found Jeff Hick’s blog post and sample scripts.

Jeff had a ZIP file with several sample PowerShell scripts. One of them, Demo-WordReport.ps1, was extremely helpful. Jeff’s comments in his code should explain what the script is doing.

write-verbose "Load Word Templates"
$word.Templates.LoadBuildingBlocks()
If ( $WordVersion -eq 12)
{
	$BuildingBlocks=$word.Templates | Where {$_.name -eq "Building Blocks.dotx"}
	$part=$BuildingBlocks.BuildingBlockEntries.Item($CoverPage)
}
Else
{
	$BuildingBlocks=$word.Templates | Where {$_.name -eq "Built-In Building Blocks.dotx"}
	$part=$BuildingBlocks.BuildingBlockEntries.Item($CoverPage)
}

write-verbose "Create empty word doc"
$Doc = $Word.Documents.Add()
$global:Selection = $Word.Selection

#insert new page, getting ready for table of contents
write-verbose "insert new page, getting ready for table of contents"
$part.Insert($selection.Range,$True) | out-null
$selection.InsertNewPage()

#table of contents
write-verbose "table of contents"
$toc=$BuildingBlocks.BuildingBlockEntries.Item("Automatic Table 2")
$toc.insert($selection.Range,$True) | out-null

#set the footer
write-verbose "set the footer"
[string]$footertext="Report created by $username"

#get the footer
write-verbose "get the footer and format font"
$doc.ActiveWindow.ActivePane.view.SeekView=$wdSeekPrimaryFooter
#get the footer and format font
$footers=$doc.Sections.Last.Footers
foreach ($footer in $footers)
{
	if ($footer.exists)
	{
		$footer.range.Font.name="Calibri"
		$footer.range.Font.size=8
		$footer.range.Font.Italic=$True
		$footer.range.Font.Bold=$True
	}
} #end Foreach
write-verbose "Footer text"
$selection.HeaderFooter.Range.Text=$footerText

#add page numbering
write-verbose "add page numbering"
$selection.HeaderFooter.PageNumbers.Add($wdAlignPageNumberRight) | Out-Null

#return focus to main document
write-verbose "return focus to main document"
$doc.ActiveWindow.ActivePane.view.SeekView=$wdSeekMainDocument

#move to the end of the current document
write-verbose "move to the end of the current document"
$selection.EndKey($wdStory,$wdMove) | Out-Null
#end of Jeff Hicks

The only change I had to make to Jeff’s script was in loading the Word Templates. Word 2007 used the name “Building Blocks.dotx”. I had to add an “If” statement to handle that condition.

write-verbose "Load Word Templates"
$word.Templates.LoadBuildingBlocks()
If ( $WordVersion -eq 12)
{
	$BuildingBlocks=$word.Templates | Where {$_.name -eq "Building Blocks.dotx"}
	$part=$BuildingBlocks.BuildingBlockEntries.Item($CoverPage)
}
Else
{
	$BuildingBlocks=$word.Templates | Where {$_.name -eq "Built-In Building Blocks.dotx"}
	$part=$BuildingBlocks.BuildingBlockEntries.Item($CoverPage)
}

In Version 1 and 2 of this script, in order to write out a line of output, my friend Michael B. Smith wrote a Line function. That function is no longer needed. Ryan took Michael’s function and modified it to write a line to Word.

To write data to Word you first use .TypeText() and then press the Enter key by using .TypeParagraph().

Function WriteWordLine
#function created by Ryan Revord
#@rsrevord on Twitter
#function created to make output to Word easy in this script
{
	Param( [int]$style=0, [int]$tabs = 0, [string]$name = '', [string]$value = '', [string]$newline = "'n", [switch]$nonewline)
	$output=""
	#Build output style
	switch ($style)
	{
		0 {"No Spacing"}
		1 {$Selection.Style = "Heading 1"}
		2 {$Selection.Style = "Heading 2"}
		3 {$Selection.Style = "Heading 3"}
		Default {"No Spacing"}
	}
	#build # of tabs
	While( $tabs -gt 0 ) {
		$output += "`t"; $tabs--;
	}

	#output the rest of the parameters.
	$output += $name + $value
	$Selection.TypeText($output)

	#test for new WriteWordLine 0.
	If($nonewline){
		# Do nothing.
	} Else {
		$Selection.TypeParagraph()
	}
}

In the original scripts, the Line function was used:

Line 1 "Administrator name: "$Administrator.AdministratorName
Line 1 "Administrator type: " –nonewline

In this modified script, the WriteWordLine function is used:

WriteWordLine 2 1 "Administrator name: "$Administrator.AdministratorName
WriteWordLine 0 1 "Administrator type: " -nonewline

The first parameter tells Word what Style to use. For the first line above, that is Heading 2. For the second line, that is No Spacing. The second parameter is how many tabs to indent.

The next changes in Version 3 come in the Servers section. I added a Word Table to hold information on the Citrix services. I know I can get more data on the services by using WMI Queries but in a large farm with XenApp servers spread geographically; those queries can cause excessive network traffic. I am only using the Get-Service cmdlet because it is native and also supports remoting.

#list citrix services
write-verbose "Processing Citrix services for server $($server.ServerName)"
$services = get-service -ComputerName $server.ServerName | where-object {$_.DisplayName -like "*Citrix*"} | sort-object DisplayName
WriteWordLine 0 2 "Citrix Services"
write-verbose "Create Word Table for Citrix services"
$TableRange = $doc.Application.Selection.Range
$Columns = 2
$Rows = $services.count + 1
$Table = $doc.Tables.Add($TableRange, $Rows, $Columns)
$table.AutoFitBehavior(1)
$table.Style = "Table Grid"
$table.Borders.InsideLineStyle = 1
$table.Borders.OutsideLineStyle = 1
$xRow = 1
$Table.Cell($xRow,1).Shading.BackgroundPatternColor = $wdColorGray15
$Table.Cell($xRow,1).Range.Font.Bold = $True
$Table.Cell($xRow,1).Range.Text = "Display Name"
$Table.Cell($xRow,2).Shading.BackgroundPatternColor = $wdColorGray15
$Table.Cell($xRow,2).Range.Font.Bold = $True
$Table.Cell($xRow,2).Range.Text = "Status"
ForEach($Service in $Services)
{
	$xRow++
	$Table.Cell($xRow,1).Range.Text = $Service.DisplayName
	$Table.Cell($xRow,2).Range.Text = $Service.Status
}
#return focus back to document
$doc.ActiveWindow.ActivePane.view.SeekView=$wdSeekMainDocument

#move to the end of the current document
$selection.EndKey($wdStory,$wdMove) | Out-Null

What I have not been able to figure out how to do is to move the Table to the right three indents. I recorded a Word macro, converted it to PowerShell and it did not work.

The Word Macro:

Selection.Tables(1).Select
Selection.Paragraphs.Indent
Selection.Paragraphs.Indent
Selection.Paragraphs.Indent

My PowerShell code:

$Table.Range.Select()
$Selection.Tables.Indent
$Selection.Tables.Indent
$Selection.Tables.Indent

I can see the Table get selected but it is not moved by three indents. If anyone knows how to do that, please let me know. I will update the script and give you credit.

The next change is to determine if Hotfix Rollup Pack 1 is installed. If not, compare the hotfixes installed to the recommended list from CTX129229 (dated 18-DEC-2012).

$HotfixArray = ""
$HRP1Installed = $False
WriteWordLine 0 2 "Citrix Hotfixes:"
ForEach($hotfix in $hotfixes)
{
	$HotfixArray += $hotfix.HotfixName
	If( $hotfix.HotfixName -eq "XA650W2K8R2X64R01")
	{
		$HRP1Installed = $True
	}
	WriteWordLine 0 3 "Hotfix`t`t`t: " $hotfix.HotfixName
	WriteWordLine 0 3 "Installed by`t`t: " $hotfix.InstalledBy
	WriteWordLine 0 3 "Installed date`t`t: " $hotfix.InstalledOn
	WriteWordLine 0 3 "Hotfix type`t`t: " $hotfix.HotfixType
	WriteWordLine 0 3 "Valid`t`t`t: " $hotfix.Valid
	WriteWordLine 0 0 ""
}
#compare Citrix hotfixes to recommended Citrix hotfixes from CTX129229
If( !$HRP1Installed )
{
	write-verbose "Processing pre HRP01 hotfix list for server $($server.ServerName)"
	#list is from CTX129229 dated 18-DEC-2012
	$RecommendedList = @("XA650W2K8R2X64001","XA650W2K8R2X64011","XA650W2K8R2X64019","XA650W2K8R2X64025")
	ForEach($element in $RecommendedList)
	{
		If(!$HotfixArray -contains $element)
		{
			#missing a recommended Citrix hotfix
			WriteWordLine 0 3 "Recommended Citrix Hotfix $element is not installed"
		}
	}
}

The same process is followed for the recommended Microsoft hotfixes.

#build list of installed Microsoft hotfixes
write-verbose "Processing Microsoft hotfixes for server $($server.ServerName)"
$MSInstalledHotfixes = Get-HotFix -computername $Server.ServerName | select-object -Expand HotFixID | sort-object HotFixID
If($server.OSServicePack.IndexOf('1') -gt 0)
{
	#Server 2008 R2 SP1 installed
	$RecommendedList = @("KB2731847","KB2571388","KB2465772", "KB917607", "KB2444328", "KB2551503", "KB2578159", "KB2620656", "KB2617858")			}
Else
{
	#Server 2008 R2 without SP1 installed
	$RecommendedList = @("KB2731847","KB2571388","KB2465772", "KB917607", "KB2444328", "KB2551503", "KB2578159", "KB2620656", "KB2617858", "KB979530", "KB975777", "KB980663", "KB2265716", "KB238928", "KB983460", "KB2388142")
}
$results = @{}
$PrintNotice = $False
foreach( $hotfix in $RecommendedList )
{
	If(!($MSInstalledHotfixes -contains $hotfix))
	{
		If(!$PrintNotice)
		{
			$PrintNotice = $True
			WriteWordLine 0 3 "Not all missing Microsoft hotfixes may be needed for this server"
		}
		#missing a recommended Citrix hotfix
		WriteWordLine 0 3 "Recommended Microsoft Hotfix $hotfix is not installed"
	}
}

Since the Citrix.GroupPolicy.commands module does not support remoting, if remoting is being used, the policy section must be skipped.

#if remoting is enabled, the citrix.grouppolicy.commands module does not work with remoting so skip it
If($Remoting)
{
	write-warning "Remoting is enabled."
	write-warning "The Citrix.GroupPolicy.Commands module does not work with Remoting."
	write-warning "Citrix Policy documentation will not take place."
}

Once all data has been gathered and placed into the Word document, the document must be finalized. First the Cover Page properties must be updated. These steps are also from Jeff Hicks.

write-verbose "Finishing up Word document"
#end of document processing
#Update document properties
write-verbose "Set Cover Page Properties"
_SetDocumentProperty $doc.BuiltInDocumentProperties "Company" $CompanyName
_SetDocumentProperty $doc.BuiltInDocumentProperties "Title" $title
_SetDocumentProperty $doc.BuiltInDocumentProperties "Subject" "XenApp 6.5 Farm Inventory"
_SetDocumentProperty $doc.BuiltInDocumentProperties "Author" $username

#Get the Coverpage XML part
$cp=$doc.CustomXMLParts | where {$_.NamespaceURI -match "coverPageProps$"}

#get the abstract XML part
$ab=$cp.documentelement.ChildNodes | Where {$_.basename -eq "Abstract"}
#set the text
[string]$abstract="Citrix XenApp 6.5 Inventory for $CompanyName"
$ab.Text=$abstract

$ab=$cp.documentelement.ChildNodes | Where {$_.basename -eq "PublishDate"}
#set the text
[string]$abstract=( Get-Date -Format d ).ToString()
$ab.Text=$abstract

The (Get-Date -Format d).ToString() will make sure the date on the Cover Page is displayed in the computer’s local date format.

Jeff’s _SetDocumentProperty function.

Function _SetDocumentProperty
{
	#jeff hicks
	Param([object]$Properties,[string]$Name,[string]$Value)
	#get the property object
	$prop=$properties | foreach {
		$propname=$_.GetType().InvokeMember("Name","GetProperty",$null,$_,$null)
		if ($propname -eq $Name)
		{
			Return $_
		}
	} #foreach

	#set the value
	$Prop.GetType().InvokeMember("Value","SetProperty",$null,$prop,$Value)
}

The final steps are to:
• Update the Table of Contents,
• Save the document,
• Close the document,
• Quit Word, and
• Perform Garbage Collection

write-verbose "Update the Table of Contents"
#update the Table of Contents
$doc.TablesOfContents.item(1).Update()

write-verbose "Save and Close document and Shutdown Word"
If ($WordVersion -eq 12)
{
	#Word 2007
	$doc.SaveAs($filename, $SaveFormat::wdFormatDocument)
}
Else
{
	$doc.SaveAs([REF]$filename, [ref]$SaveFormat::wdFormatDocument)
}

$doc.Close()
$Word.Quit()
[gc]::collect()
[gc]::WaitForPendingFinalizers()

And there you have it, that is “all” there is to creating a really nice document in Microsoft Word using PowerShell.

Since this script has parameters, I wanted to create help text for the script.

<#
.SYNOPSIS
	Creates a complete inventory of a Citrix XenApp 6.5 farm using Microsoft Word.
.DESCRIPTION
	Creates a complete inventory of a Citrix XenApp 6.5 farm using Microsoft Word and PowerShell.
	Creates a Word document named after the XenApp 6.5 farm.
	Document includes a Cover Page, Table of Contents and Footer.
.PARAMETER CompanyName
	Company Name to use for the Cover Page.
	Default value is contained in HKCU:\Software\Microsoft\Office\Common\UserInfo\Company
	This parameter has an alias of CN.
.PARAMETER CoverPage
	What Microsoft Word Cover Page to use.
	(default cover pages in Word en-US)
	Valid input is:
		Alphabet (Word 2007/2010. Works)
		Annual (Word 2007/2010. Doesn't really work well for this report)
		Austere (Word 2007/2010. Works)
		Austin (Word 2010/2013. Doesn't work in 2013, mostly works in 2007/2010 but Subtitle/Subject & Author fields need to me moved after title box is moved up)
		Banded (Word 2013. Works)
		Conservative (Word 2007/2010. Works)
		Contrast (Word 2007/2010. Works)
		Cubicles (Word 2007/2010. Works)
		Exposure (Word 2007/2010. Works if you like looking sideways)
		Facet (Word 2013. Works)
		Filigree (Word 2013. Works)
		Grid (Word 2010/2013.Works in 2010)
		Integral (Word 2013. Works)
		Ion (Dark) (Word 2013. Top date doesn't fit, box needs to be manually resized or font changed to 8 point)
		Ion (Light) (Word 2013. Top date doesn't fit, box needs to be manually resized or font changed to 8 point)
		Mod (Word 2007/2010. Works)
		Motion (Word 2007/2010/2013. Works if top date is manually changed to 36 point)
		Newsprint (Word 2010. Works but date is not populated)
		Perspective (Word 2010. Works)
		Pinstripes (Word 2007/2010. Works)
		Puzzle (Word 2007/2010. Top date doesn't fit, box needs to be manually resized or font changed to 14 point)
		Retrospect (Word 2013. Works)
		Semaphore (Word 2013. Works)
		Sideline (Word 2007/2010/2013. Doesn't work in 2013, works in 2007/2010)
		Slice (Dark) (Word 2013. Doesn't work)
		Slice (Light) (Word 2013. Doesn't work)
		Stacks (Word 2007/2010. Works)
		Tiles (Word 2007/2010. Date doesn't fit unless changed to 26 point)
		Transcend (Word 2007/2010. Works)
		ViewMaster (Word 2013. Works)
		Whisp (Word 2013. Works)
	Default value is Motion.
	This parameter has an alias of CP.
.PARAMETER UserName
	User name to use for the Cover Page and Footer.
	Default value is contained in $env:username
	This parameter has an alias of UN.
.EXAMPLE
	PS C:\PSScript > .\XA65_Inventory_v3.ps1

	Will use all default values.
	HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl Webster"
	$env:username = Administrator

	Carl Webster for the Company Name.
	Motion for the Cover Page format.
	Administrator for the User Name.
.EXAMPLE
	PS C:\PSScript > .\XA65_Inventory_v3.ps1 -verbose

	Will use all default values.
	HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl Webster"
	$env:username = Administrator

	Carl Webster for the Company Name.
	Motion for the Cover Page format.
	Administrator for the User Name.
	Will display verbose messages as the script is runnin.
.EXAMPLE
	PS C:\PSScript .\XA65_Inventory_v3.ps1 -CompanyName "Carl Webster Consulting" -CoverPage "Mod" -UserName "Carl Webster"

	Will use:
		Carl Webster Consulting for the Company Name.
		Mod for the Cover Page format.
		Carl Webster for the User Name.
.EXAMPLE
	PS C:\PSScript .\XA65_Inventory_v3.ps1 -CN "Carl Webster Consulting" -CP "Mod" -UN "Carl Webster"

	Will use:
		Carl Webster Consulting for the Company Name (alias CN).
		Mod for the Cover Page format (alias CP).
		Carl Webster for the User Name (alias UN).
.INPUTS
	None.  You cannot pipe objects to this script.
.OUTPUTS
	No objects are output from this script.  This script creates a Word document.
.LINK

http://www.carlwebster.com/documenting-a-citrix-xenapp-6-5-farm-with-microsoft-powershell-and-word-version-3

.NOTES
	NAME: XA65_Inventory_V3.ps1
	VERSION: 3
	AUTHOR: Carl Webster (with a lot of help from Michael B. Smith and Jeff Wouters)
	LASTEDIT: January 27, 2013
#>

Running the following command from the PowerShell prompt will display the full help text.

Get-Help .\XA65_Inventory_v3.ps1 -full

Sample output:

PS C:\webster> Get-Help .\xa65_Inventory_V3.ps1 -full

NAME
    C:\webster\XA65_Inventory_V3.ps1

SYNOPSIS
    Creates a complete inventory of a Citrix XenApp 6.5 farm using Microsoft Word.

SYNTAX
    C:\webster\XA65_Inventory_V3.ps1 [[-CompanyName] ] [[-CoverPage] ] [[-UserName] ] []

DESCRIPTION
    Creates a complete inventory of a Citrix XenApp 6.5 farm using Microsoft Word and PowerShell.
    Creates a Word document named after the XenApp 6.5 farm.
    Document includes a Cover Page, Table of Contents and Footer.

PARAMETERS
    -CompanyName
        Company Name to use for the Cover Page.
        Default value is contained in HKCU:\Software\Microsoft\Office\Common\UserInfo\Company
        This parameter has an alias of CN.

        Required?                    false
        Position?                    1
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?

    -CoverPage
        What Microsoft Word Cover Page to use.
        (default cover pages in Word en-US)
        Valid input is:
            Alphabet (Word 2007/2010. Works)
            Annual (Word 2007/2010. Doesn't really work well for this report)
            Austere (Word 2007/2010. Works)
            Austin (Word 2010/2013. Doesn't work in 2013, mostly works in 2007/2010 but Subtitle/Subject & Author fields need to me moved after title box is moved up)
            Banded (Word 2013. Works)
            Conservative (Word 2007/2010. Works)
            Contrast (Word 2007/2010. Works)
            Cubicles (Word 2007/2010. Works)
            Exposure (Word 2007/2010. Works if you like looking sideways)
            Facet (Word 2013. Works)
            Filigree (Word 2013. Works)
            Grid (Word 2010/2013.Works in 2010)
            Integral (Word 2013. Works)
            Ion (Dark) (Word 2013. Top date doesn't fit, box needs to be manually resized or font changed to 8 point)
            Ion (Light) (Word 2013. Top date doesn't fit, box needs to be manually resized or font changed to 8 point)
            Mod (Word 2007/2010. Works)
            Motion (Word 2007/2010/2013. Works if top date is manually changed to 36 point)
            Newsprint (Word 2010. Works but date is not populated)
            Perspective (Word 2010. Works)
            Pinstripes (Word 2007/2010. Works)
            Puzzle (Word 2007/2010. Top date doesn't fit, box needs to be manually resized or font changed to 14 point)
            Retrospect (Word 2013. Works)
            Semaphore (Word 2013. Works)
            Sideline (Word 2007/2010/2013. Doesn't work in 2013, works in 2007/2010)
            Slice (Dark) (Word 2013. Doesn't work)
            Slice (Light) (Word 2013. Doesn't work)
            Stacks (Word 2007/2010. Works)
            Tiles (Word 2007/2010. Date doesn't fit unless changed to 26 point)
            Transcend (Word 2007/2010. Works)
            ViewMaster (Word 2013. Works)
            Whisp (Word 2013. Works)
        Default value is Motion.
        This parameter has an alias of CP.

        Required?                    false
        Position?                    2
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?

    -UserName
        User name to use for the Cover Page and Footer.
        Default value is contained in $env:username
        This parameter has an alias of UN.

        Required?                    false
        Position?                    3
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?

        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer and OutVariable. For more information, type,
        "get-help about_commonparameters".

INPUTS
    None.  You cannot pipe objects to this script.

OUTPUTS
    No objects are output from this script.  This script creates a Word document.

NOTES

        NAME: XA65_Inventory_V3.ps1
        VERSION: 3
        AUTHOR: Carl Webster (with a lot of help from Michael B. Smith and Jeff Wouters)
        LASTEDIT: January 27, 2013

    -------------------------- EXAMPLE 1 --------------------------

    PS C:\PSScript >.\XA65_Inventory_v3.ps1

    Will use all default values.
    HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl Webster"
    $env:username = Administrator

    Carl Webster for the Company Name.
    Motion for the Cover Page format.
    Administrator for the User Name.

    -------------------------- EXAMPLE 2 --------------------------

    PS C:\PSScript >.\XA65_Inventory_v3.ps1 -verbose

    Will use all default values.
    HKEY_CURRENT_USER\Software\Microsoft\Office\Common\UserInfo\Company="Carl Webster"
    $env:username = Administrator

    Carl Webster for the Company Name.
    Motion for the Cover Page format.
    Administrator for the User Name.
    Will display verbose messages as the script is runnin.

    -------------------------- EXAMPLE 3 --------------------------

    C:\PS>PS C:\PSScript .\XA65_Inventory_v3.ps1 -CompanyName "Carl Webster Consulting" -CoverPage "Mod" -UserName "Carl Webster"

    Will use:
        Carl Webster Consulting for the Company Name.
        Mod for the Cover Page format.
        Carl Webster for the User Name.

    -------------------------- EXAMPLE 4 --------------------------

    C:\PS>PS C:\PSScript .\XA65_Inventory_v3.ps1 -CN "Carl Webster Consulting" -CP "Mod" -UN "Carl Webster"

    Will use:
        Carl Webster Consulting for the Company Name (alias CN).
        Mod for the Cover Page format (alias CP).
        Carl Webster for the User Name (alias UN).

RELATED LINKS

http://www.carlwebster.com/documenting-a-citrix-xenapp-6-5-farm-with-microsoft-powershell-and-word-version-3

PS C:\webster>

You can also use –online to get taken to this article.

Get-Help .\XA65_Inventory_V3.ps1 -online

Running the script with –verbose, gives information of the script’s running.

.\xa65_inventory_v3.ps1 -CompanyName "The Accidental Citrix Admin" -CoverPage "Whisp" -UserName "Amalgamated Consulting Group" -verbose

Sample output.

PS C:\webster> .\xa65_inventory_v3.ps1 -CompanyName "The Accidental Citrix Admin" -CoverPage "Whisp" -UserName "Amalgamated Consulting Group" -verbose
VERBOSE: Company Name: The Accidental Citrix Admin
VERBOSE: Cover Page  : Whisp
VERBOSE: User Name   : Amalgamated Consulting Group
VERBOSE: Remoting is not being used
VERBOSE: Getting Farm data
VERBOSE: Setting up Word
VERBOSE: Create Word comObject.  If you are not running Word 2007, ignore the next message.
VERBOSE: The object written to the pipeline is an instance of the type "Microsoft.Office.Interop.Word.ApplicationClass" from the component's primary interop assembly. If this type exposes different members than the IDispatch members, scripts written to work with this object might not work if the primary interop assembly is not installed.
VERBOSE: Running Microsoft Word 2013
VERBOSE: Validate cover page
VERBOSE: Load Word Templates
VERBOSE: Create empty word doc
VERBOSE: insert new page, getting ready for table of contents
VERBOSE: table of contents
VERBOSE: set the footer
VERBOSE: get the footer and format font
VERBOSE: Footer text
VERBOSE: add page numbering
VERBOSE: return focus to main document
VERBOSE: move to the end of the current document
VERBOSE: Processing Configuration Logging
VERBOSE: Processing Administrators
VERBOSE: Processing Applications
VERBOSE: Processing Configuration Logging/History Report
VERBOSE: ParameterSet = ByConnectionString
VERBOSE: Processing Load Balancing Policies
WARNING: Load balancing policy information could not be retrieved
VERBOSE: Processing Load Evaluators
VERBOSE: Processing Servers
VERBOSE: Processing Citrix services for server XA651
VERBOSE: Create Word Table for Citrix services
VERBOSE: Processing Microsoft hotfixes for server XA651
VERBOSE: Processing Citrix services for server XA652
VERBOSE: Create Word Table for Citrix services
VERBOSE: Processing Microsoft hotfixes for server XA652
VERBOSE: Processing Citrix services for server XA653
VERBOSE: Create Word Table for Citrix services
VERBOSE: Processing Microsoft hotfixes for server XA653
VERBOSE: Processing Citrix services for server XA654
VERBOSE: Create Word Table for Citrix services
VERBOSE: Processing Microsoft hotfixes for server XA654
VERBOSE: Processing Worker Groups
VERBOSE: Processing Zones
VERBOSE: Processing Citrix IMA Policies
VERBOSE: Finishing up Word document
VERBOSE: Set Cover Page Properties
VERBOSE: Update the Table of Contents
VERBOSE: Save and Close document and Shutdown Word
PS C:\webster>

How to use this script?

I saved the script as XA65_Inventory_V3.ps1 in the C:\PSScripts folder. From the PowerShell prompt, change to the C:\PSScripts folder, or the folder where you saved the script. From the PowerShell prompt, type in:

.\XA65_Inventory_V3.ps1 and press Enter.

If you have any suggestions for the script, please let me know. Send an e-mail to webster@carlwebster.com.

NOTE: This script is continually updated.  You can always find the most current version by going to http://carlwebster.com/where-to-get-copies-of-the-documentation-scripts/

Copies of all the Cover Pages can be found here:
https://dl.dropbox.com/u/43555945/Word2007SampleCoverPages.zip
https://dl.dropbox.com/u/43555945/Word2010SampleCoverPages.zip
https://dl.dropbox.com/u/43555945/Word2013SampleCoverPages.zip

, , ,

About Carl Webster

Webster is an independent consultant in the Nashville, TN area and specializes in Citrix, Active Directory and Technical Documentation. Webster has been working with Citrix products for many years starting with Multi-User OS/2 in 1990.

View all posts by Carl Webster

12 Responses to “Documenting a Citrix XenApp 6.5 Farm with Microsoft PowerShell and Word – Version 3”

  1. David Says:

    The dropbox link site cannot be found. IS the latest script still available somewhere? I would love to try it against our environment.

    Thank you.

    Reply

    • Carl Webster Says:

      Thanks for reminding me I needed to go through every documentation script related article and update the links. I have updated every article with a new standard link.

      Webster

      Reply

  2. Chaitanyakumar G Says:

    Hi Carl,

    I have designed powershell script to install xenapp 6(3 versions, added more feasibility in every version), and now I have created posh script for xenapp 6.5. I used your script given in this site. I have got only one thing to say. YOU ARE AWESOME!!! great…

    Chaitanya.

    Reply

  3. Mehdi Says:

    hi,

    I have a farm XenApp 6.5 and i want to try your script.

    But i don’t know where I can find your “Building Blocks.dotx” ?
    I have only : “Built-In Building Blocks.dotx”

    My Word 2010 version is in french.

    Thank you.

    Reply

    • Carl Webster Says:

      Unfortunately, the current scripts only support English versions of Microsoft Word. Support for non-English versions of Word is planned for the V4 script.

      Sorry

      Webster

      Reply

  4. Patrick Coughlin Says:

    I had an issue with the 6.5 V3 documentation script crashing out in a customer environment due to accumulated spell check autocorrect processing. The script would fail half way through the farm and eventually an error dialog from word would appear. I resolved it by explicitly disabling the spell checker and grammar checker in the script. I added the following after line # 524 in the unsigned script.

    522 write-verbose “Create empty word doc”
    523 $Doc = $Word.Documents.Add()
    524 $global:Selection = $Word.Selection
    525
    526 #Disable Spell and Grammer Check to resolve issue and improve performance
    527 $Word.Options.CheckGrammarAsYouType=$false
    528 $Word.Options.CheckSpellingAsYouType=$false

    Reply

    • Carl Webster Says:

      Sweet! Thanks for the tip. I will add this to the next version of the script I will be releasing next week.

      Webster

      Reply

  5. Donovan Sobrero Says:

    If the farm doesn’t have office installed on any of the servers is it possible to run this from a workstation that may have the Appcenter on it?

    Reply

    • Carl Webster Says:

      Yes, this should work provided:

      On the XenApp Server, Firewall rule created to allow inbound TCP Port 2513 from your Win7 PC
      AppCenter is installed on the client
      You may need the XenApp PoSH SDK installed on the client (mine did)
      Word installed on the client
      On the client run Set-XADefaultComputerName XA65ControllerServerName

      Except for the Citrix Group Policy stuff, the script should then run just fine.

      Thanks

      Webster

      Reply

  6. Tom Says:

    Whenever time permits you could comment upon which cover sheets are included with Word 2010/2013 and where/how to go look at them without running the script 34,445 times :) :)

    Otherwise, totally awesome gift to the Citrix community.

    Thank you, Tom

    P.S. Put Submit button BELOW the math question, please. :)

    Reply

    • Carl Webster Says:

      At the end of the article are links to sample cover sheets for all three versions of Word. I already ran the script 34,445 times! :)

      Webster

      Reply

Leave a Reply

Current month ye@r day *