Intro
One of the signs of a successful IT team is to have clearly defined environments for development, testing, and production. To actually put this into practice however, requires enough head count and enough resources (real and virtual) to maintain it. Many Citrix shops don’t have these luxuries. In this post, we will look at a script I wrote to help cleanup the users/groups assigned to Published Applications in a XenApp 6.5 farm that was used for development, testing, and production.
Hey, we need to test an app…
This phrase is met with trepidation by many Citrix Admins. Your co-worker has just sent an email with scant details and many assurances that this application test is just a POC and won’t have many users or require many resources. So you (being awesome) get the application installed and working and the POC starts.
First, you add one or two users, then more and more, then a group that holds most of the department that wanted to test this application to being with. Within a month, you’re told the POC was wildly successful and they already purchased the software. Now you have an application with a “Configured Users” section that is out of control

So, if you have a number of applications like this how can you clean things up or at least get some feedback on what should change?
Putting It Together
Loops and loops
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Get list of applications | |
$XAApps = Get-XAApplicationReport –ComputerName $xmlbroker * | where {$_.Enabled -eq $true} | select browsername,accounts | |
foreach ($app in $XAApps) { | |
write-verbose $app.browsername | |
write-verbose (($app.Accounts | select accountname).accountname | out-string) | |
$isUser = $null | |
$isGroup = $null | |
# Application account(s) loop | |
foreach ($acct in $app.Accounts) { | |
if ($acct.AccountName -match $accountpattern) { | |
Write-verbose "$acct is a user account" | |
$isUser += $acct.AccountName.ToString() + ',' | |
} else { | |
write-verbose "$acct is an AD group" | |
if ($acct -match 'Citrix Admins') { | |
write-verbose "AD Group is Citrix Admins" | |
} else { | |
$isGroup += $acct.AccountName.ToString() + ',' | |
} | |
} | |
} |
We save all the active applications to a variable and just keep the application name and assigned accounts. Then we start looping through the accounts for each application. If the account matches the regular expression (see the $accountpattern variable in the full script below) for user accounts, then it is added to the $isUser variable. Otherwise, we add the AD Group to the $isGroup variable (skipping the “Citrix Admins” group as it is already added to every application).
Act on the information
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
if ($isUser -ne $null) { | |
$isUser = $isUser.TrimEnd(",") | |
if ($isGroup -ne $null) { | |
$isGroup = $isGroup.TrimEnd(",") | |
$usrCount = 0 | |
foreach ($grp in $isGroup.split(",")) { | |
$grpUsers = (Get-ADGroupMember $grp | select samaccountname).samaccountname | |
foreach ($usr in $isUser.split(",")) { | |
if ($grpUsers -contains $usr) { | |
$xaapp = $app.browsername.ToString() | |
write-warning "$usr is already a member of $grp. REMOVE from $xaapp" | |
add-content $outputfile –value "$usr should be removed from $xaapp" | |
$usrCount++; | |
} | |
} | |
} | |
if ($usrCount -eq 0) { | |
$xaapp = $app.browsername.ToString() | |
write-warning "$isuser is/are NOT a member of any '$xaapp' groups." | |
add-content $outputfile –value "$isuser should be added to new CTX-$xaapp group" | |
if ($addADGroupList) {add-content $adgroupfile –value "CTX-$xaapp"} | |
} | |
} else { | |
$xaapp = $app.browsername.ToString() | |
Write-warning "$xaapp needs an AD Group. For example — CTX-$xaapp —" | |
add-content $outputfile –value "$xaapp needs an AD Group. For example — CTX-$xaapp —" | |
if ($addADGroupList) {add-content $adgroupfile –value "CTX-$xaapp"} | |
} | |
} else { | |
if ($isGroup -ne $null) { | |
$isGroup = $isGroup.TrimEnd(",") | |
if ($isGroup.count -ge 1) { | |
$xaapp = $app.browsername.ToString() | |
Write-warning "$xaapp needs a single AD Group. For example — CTX-$xaapp —" | |
add-content $outputfile –value "$xaapp needs an AD Group. For example — CTX-$xaapp —" | |
if ($addADGroupList) {add-content $adgroupfile –value "CTX-$xaapp"} | |
} | |
} else { | |
$xaapp = $app.browsername.ToString() | |
Write-warning "$xaapp has no users or groups assigned. It needs an AD Group. For example — CTX-$xaapp —" | |
add-content $outputfile –value "$xaapp needs an AD Group. For example — CTX-$xaapp —" | |
if ($addADGroupList) {add-content $adgroupfile –value "CTX-$xaapp"} | |
} | |
} | |
} |
Keep in mind that we are still in the second account foreach loop. Review the $isUser and $isGroup variables. If a user is a member of one of the groups already assigned to the application, make a note in a text file to remove the user from the application. If a user is not a member of any groups, then recommend that a new AD group be created for this application and add those users to it. Otherwise, if there are no groups associated with the application, recommend that one get created. You can step through the if..else logic with some test applications and add actual active directory actions to further automate the script.
At the end you will end up with a text file (two if you use the $addADGroupList switch variable). The xaappfixes_datetimestamp.txt file has a list of users to remove from groups and suggested AD (Active Directory) groups that should be created. The xaappgroups_datetimestamp.txt just lists suggested AD groups that you could run through a AD script or hand over to your AD team.
Here is an example of the xaappfixes file
Here is an example of the xaappgroups file
The Script
You can get the script from GitHub
Thanks for reading,
Alain Assaf