David Ott

Jul 212016
 

CLICK HERE FOR UPDATED VERSION

My boss wanted a report to show the usage of our XenDesktop environment (desktop sessions only), so I wrote a script that queries the delivery controller once per minute to get sessions.  It then writes that information to an .xml file, and then I have another script that reads the .xml file to report on usage.  Well, that is a bit complicated, and all that data already exists in the ODATA in director.  I have demonstrated how to query that data in a previous script I wrote to get logon times, but gathering each web table and then combining can take a long time.

That ODATA information is also in the monitordata tables of your Citrix database (which is split off into a separate database from the main database after 7.8 I think), and querying SQL is A LOT faster!

The script I wrote needs to be run as a user who has at least read rights to the database.  It will query the monitordata tables and combine the information into an array which can output the information we want.  For example if you query monitordata.session it will give you all the sessions, but the only indication of which user launched that session is the UserID field… which is just a number.  You have to join monitordata.session and monitordata.[user] in order to figure out who it actually was in that session.  The query I wrote actually gets a lot more information than is needed for this script, so I can use it as a basis to write more scripts.  Feel free to use this as a base for your own scripts… if you do note that all the date/time values in the database are UTC time.

Here is the script!  Be sure to pay attention to anything marked with ####

Here is an example report email.

Capture

Jun 212016
 

This will be short and sweet (hopefully sweet).  If you have a session recording server setup you may notice that the entries/files don’t go away on their own.  Here is how to clean them up:

Just create a scheduled task to run the code below once per day (as system – elevated):

C:\Program Files\Citrix\SessionRecording\Server\Bin\icldb.exe remove /RETENTION:7 /DELETEFILES /F /S /L

remove = remove from the database
/RETENTION = days of files to keep (above code will keep 7 days)
/DELETEFILES = delete files as well
/F = Force
/S = Suppress copyright
/L = Log to event log

Me being the powershell lover that I am created a script (one liner below), saved the .ps1, and created a scheduled task to run powershell calling my script.
StartProcess -FilePath ‘C:\Program Files\Citrix\SessionRecording\Server\Bin\icldb.exe’ -ArgumentList “remove /RETENTION:7 /DELETEFILES /F /S /L”

May 062016
 

If you have tried to use Windows 10 in XenDesktop with Citrix Profile Management you have probably run into two major issues.

The Issues
The first issue is the start menu… which is now a database located at %localappdata%\TileDataLayer\Database.  At logoff when profile manager tries to copy it off it can’t due to services locking the files.  This results in the user logging on and their start menu not working.

The second issue revolves around SMB2/3.  SMB1 would close files as soon as it was done with them, but 2/3 leave them open for a little longer in case they are requested again.  This means when a user logs off and their Pooled Random desktop shuts down file locks can remain in the profile store if the shutdown process happens too fast (which it does 99% of the time).  Basically, if a user was to logoff and then try to log back on in a short period of time their logon would be greatly delayed due to the “ghost” file locks.

The Workarounds
Start Menu – This one is a bit tricky.  When a user logs off we need to stop the Tile Data model server and State Repository Service (in that order) so that profile manager can copy the start menu database off to the user store.  Here is the rub… a normal user cannot stop these services, so you can’t use a logoff script!  Here is what you do… logon as a local administrator and…

  1. Create a powershell script on the root of C:\ – name it logoff.ps1
    1. Open powershell_ise.exe as administrator and write these 2 lines in the white space at the top (if no white space hit the new button to create a new script).
      stop-service tiledatamodelsvc -force
      stop-service staterepository -force
    2. Save it as C:\logoff.ps1 (or put it where ever you want – just remember where it is)
  2. Right click the start menu – hit run – type in taskschd.msc and hit ok
  3. Right click the Task Scheduler Library node and select Create Basic Task…
  4. Name it whatever you want – I named mine logoff – hit next
  5. Select “When a specific event is logged” on the next screen and hit next
  6. Under Log: start typing “Sec”  the Security log should show up
  7. Under Source type in “Microsoft Windows security auditing” (no quotes)
  8. Event ID will be 4647 – hit next
  9. leave Start a program selected – hit next
  10. in the program/script blank C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
  11. in the add arguments blank “-executionpolicy unrestricted -file c:\logoff.ps1” (no quotes) – if you put the script somewhere else make sure you have the path correct.
  12. click next, and on the next screen check the box “Open the Properties dialog…” and hit Finish
  13. Click Change User or Group, type in “system” (no quotes) and hit ok
  14. Check “Run with highest privileges”, and hit ok

Now when a user initiates a logoff the system will stop the Tile Data model server and State Repository Service.  This will allow profile manager to copy off the start menu database.

Ghost File locks
This one is pretty easy – we just delay the shutdown with a shutdown script.  This allows the file locks to be released at shutdown.  Here is what you do… (you should still be logged on as a local administrator)

  1. Run powershell_ise.exe as administrator and type these 2 lines in the white space at the top… again if no white space hit the new button.
    stop-service brokeragent -force
    start-sleep -s 30
  2. Save it as C:\shutdown.ps1 (or where ever you want)
  3. Right click the start menu – hit run – type in gpedit.msc
  4. Under “Computer Configuration\Windows Settings\Scripts” double click on Shutdown
  5. Click the PowerShell Scripts tab
  6. Click Add – browse to the script you just created – hit ok
  7. Hit Ok again on the shutdown properties box, and close the local group policy editor

At shutdown this script will kill the brokeragent service (just in case delaying the shutdown would allow the desktop to appear “available” again), and delay the shutdown by 30 seconds.  This allows all file locks in the profile manager store to be released.

Bonus – UPM policy settings for Windows 10 (These are mine, so you may need to tweak for your environment – of course redirect all you can)

Exclusion list (registry) 
Software\Microsoft\Office\15.0\Excel\Resiliency
Software\Microsoft\Office\15.0\PowerPoint\Resiliency
Software\Microsoft\Office\15.0\Word\Resiliency
Software\Microsoft\Office\15.0\OneNote\Resiliency
Software\Microsoft\Office\15.0\Outlook\Resiliency
Software\Microsoft\Internet Explorer\Recovery

Exclusion list – directories
$Recycle.Bin
$Recycle.Bin
AppData\Local\Microsoft\Windows\Burn
AppData\Local\Microsoft\Windows Live
AppData\Local\Microsoft\Windows Live Contacts
AppData\Local\Microsoft\Terminal Server Client
AppData\Local\Microsoft\Messenger
AppData\Local\Microsoft\OneNote
AppData\Local\Windows Live
AppData\Local\Sun
AppData\Local\Google\Chrome\User Data\Default\Cache
AppData\Local\Microsoft\Windows\Temporary Internet Files
AppData\Local\Temp
AppData\LocalLow
AppData\Roaming\Sun\Java\Deployment\cache
AppData\Roaming\Sun\Java\Deployment\log
AppData\Roaming\Sun\Java\Deployment\tmp
AppData\Roaming\Citrix\PNAgent\AppCache
AppData\Roaming\Citrix\PNAgent\Icon Cache
AppData\Roaming\Citrix\PNAgent\ResourceCache
AppData\Roaming\ICAClient\Cache
AppData\Roaming\Macromedia\Flash Player\macromedia.com\support\flashplayer\sys
AppData\Roaming\Macromedia\Flash Player\#SharedObjects
AppData\Roaming\Microsoft\Excel
AppData\Local\Microsoft\Internet Explorer\Recovery
AppData\Roaming\Microsoft\Word
AppData\Roaming\Microsoft\Powerpoint
AppData\Local\Microsoft\Windows Mail
AppData\Local\Microsoft\Office\15.0\OfficeFileCache
AppData\Roaming\Dropbox
AppData\Local\Dropbox
Dropbox
AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Dropbox
Sharefile
AppData\Roaming\Microsoft\Templates\LiveContent
AppData\Local\Downloaded Installations
AppData\Local\Cisco\Unified Communications\Jabber\CSF\Voicemail
AppData\Local\Cisco\Unified Communications\Jabber\Voicemail
AppData\Local\Microsoft\Windows\Themes
AppData\Local\Microsoft\Windows\WER
AppData\Local\Microsoft\Windows\WebCache.old
AppData\Local\ATT Connect
AppData\Roaming\Sharefile\Outlook
AppData\Roaming\com.adobe.formscentral.FormsCentralForAcrobat
AppData\Local\Skype
AppData\Local\Assembly\dl3
AppData\Local\Cisco\Unified Communications\Jabber\Crash Dump
AppData\Local\Cisco\Unified Communications\Jabber\CSF\Logs
AppData\Roaming\Microsoft\Internet Explorer\UserData
AppData\Roaming\Spotify
AppData\Local\Spotify
AppData\Local\Microsoft\Windows\PriCache
AppData\Local\Packages
AppData\Local\Microsoft\Windows\Application Shortcuts
OneDrive
AppData\Local\Microsoft\CLR-v4.0_32
AppData\Local\Microsoft\GameDVR
AppData\Local\Microsoft\Group Policy
AppData\Local\Microsoft\Media Player
AppData\Local\Microsoft\OneDrive
AppData\Local\Microsoft\PlayReady
AppData\Local\Microsoft\Windows\1033
AppData\Local\Microsoft\Windows\Caches
AppData\Local\Microsoft\Windows\Explorer
AppData\Local\Microsoft\Windows\GameExplorer
AppData\Local\Microsoft\Windows\Notifications
AppData\Local\Microsoft\Windows\Ringtones
AppData\Local\Microsoft\Windows\RoamingTiles
AppData\Local\Comms

Exclusion list – files
AppData\Local\Microsoft\Windows\UsrClass.dat*
*thumb*.db
*icon*.db

Files to synchronize
AppData\Local\Microsoft\Office\*.qat
AppData\Local\Microsoft\Office\*.officeUI
AppData\LocalLow\Google\GoogleEarth\*.kml
AppData\Roaming\Microsoft\Excel\Excel*.xlb
AppData\LocalLow\Sun\Java\Deployment\deployment.properties
AppData\Roaming\ShareFile\Outlook\config.cfg
AppData\Roaming\ShareFile\Outlook\log.txt

Directories to synchronize
AppData\Roaming\Microsoft\Credentials
AppData\Roaming\Microsoft\Crypto
AppData\Roaming\Microsoft\Protect
AppData\Roaming\Microsoft\SystemCertificates
AppData\Local\Microsoft\Credentials
AppData\Roaming\Microsoft\Excel\XLSTART
AppData\Roaming\Microsoft\Word\STARTUP
AppData\LocalLow\Sun\Java\Deployment\ext
AppData\LocalLow\Sun\Java\Deployment\security

Folders to mirror
AppData\Local\Microsoft\Windows\INetCookies
AppData\Local\Microsoft\Windows\WebCache
AppData\Roaming\Microsoft\Windows\Cookies

Process Internet Cookie files on logoff  – Enabled

Process logons of local administrators – Enabled

Profile streaming – Enabled

Path to user store – \\server\share\%username%.%userdomain%\!CTX_PROFILEVER!!CTX_OSBITNESS!

Enable Profile management – Enabled

One more bonus!!
Based mostly on this Citrix blog – Windows 10 Optimization for XenDesktop – I wrote a powershell script to automatically optimize your Windows 10 gold PVS image… available HERE.

Feb 242016
 

Have you ever looked at director and saw a user had a very long login, and wondered how the hell you could find out who that was?  I wrote a script to help you out a bit!

Here is the script

Read it!  There are places you will have to edit to allow it to run in your environment.  Search for ##### to find the spots to pay attention to.

In the end it will output a .htm file to your my documents\logon_logs and open after it finishes.

 

Note: I have no idea if this works for XA, but I know it works for XD 7.6

Nov 242015
 

Have you ever excluded a directory after the fact, and realized that Citrix Profile Manager does not remove the directory from the store? Have you noticed “AppData\Local\Microsoft\Internet Explorer\DOMStore” or “AppData\Roaming\Microsoft\Windows\Recent” filling up with tons of small files/empty folders – most of which are old as dirt (delaying logon)? Have you pulled out your hair over the tons of junk OICE folders showing up in AppData? I have a solution for all of this mess… I wrote another script!

What you need:

Powershell of course – minimum v2 I think
An account which has rights to go through all of the profiles and delete the junk files/folders (run the script as that user if it isn’t you).
A .csv file with your exclusion/sync list (read the CSV portion in the comments of the script – muy importante!)

Here is the script

 

Oct 192015
 

Sacha Thomet who was one of my competitors for the “Geekovation” contest at Synergy (he won!) wrote a PVS documentation script (http://blog.appcloud.ch/citrix-pvs-healthcheck/).  He tweeted a revision, and after looking at the code I decided to try my hand at it.  I have to give credit to Remko Weijnen (http://www.remkoweijnen.nl/blog/2012/02/29/convert-mcli-output-into-powershell-objects/) for the code to change the mcli text output into arrays, and to Martin Pugh for the code to make a pretty web report (website and info in the comments of the function).

The script requires the mcli pssnapin (https://www.citrix.com/blogs/2011/01/11/pvs-powershell-mclipssnapin/)
Here is the script http://pastebin.com/p5qBAseZ 
Edit line 131 to set the path to the html output file.
Add “invoke-expression $htm” at the end if you want it to auto launch the html report.

Feel free to edit the script – the html output is kinda dirty but it works.

pvs report

Aug 312015
 

Another Netscaler – Powershell script leveraging Nitro!
This script will create a Cipher Group with all the right Cipher Suites (depending on VPX\MPX), or let you select one you have already created, and assign it to any ssl vserver.  NOTE:  Very important!!  If you do this to a XD/XA gateway – all users connected through that gateway will be disconnected!!  (they can of course just re-connect).  Below is a video from my test environment.  You will see that “TestCGN” Cipher Group does not exist.  I create it, and then select my owa ssl vserver as the vserver to bind it to (I could have selected them all).  The link to the script is below the video – remember TEST before using it in production!

Edit: Completely slipped my mind to disable SSLv3.  I updated the script to disable SSLv3/TLS1, and enable TLS1.1/1.2.  If you have an SSL Profile set this might fail.  Fixed that too :-)… and updated the video.

Script

Aug 282015
 

This post will be short and sweet.  I just wanted to post my first attempt at Powershell scripting against Netscaler.  Click here

Read the comments!

This script will connect to your netscaler (I have it do it on a gui enabled snip instead of the nsip, but  you can do it against the nsip… again read the comments).  If the NS is in a HA pair it will report if they are up or down.  Grab all vservers – report up/down/degraded… if degraded it will tell you which service is down.  Then it grabs all the gateways – reports up or down.  Finally it looks for any ica sessions, and will report if they are using Framehawk or not (requires NS 11.0 62.10 or above).

May 072015
 

Just a quick script to grab XenDesktop 7.x licensing info… would probably work on XA 7.x as well.  As the script is written it has to be run on the licensing server itself, but you could easily rewrite it with an “Invoke-Command” to allow it to run remotely.  You could also modify it to email you when the licenses go above a certain threshold, and set it as a scheduled task.  The script just does “get” commands, so there is no risk (as it is) to your environment… go nuts.

http://pastebin.com/97C7kxHq

Mar 202015
 

EDIT:  People have been requesting a tool to deploy SMS2 secret keys en mass, and the developer hasn’t implemented it yet.  Until he does I wrote a powershell script that will remotely connect to the sql database and inject the information needed for each user you select (http://pastebin.com/NBJHJPsX).  I have it setup for TOTP keys… which I think is what most people will use.
EDIT2:  I created a new script that does basically the same thing as the script posted above, but you can direct it against a specific AD group (http://pastebin.com/L9D8Jwaf).  Also, if you haven’t yet – upgrade your netscalers to version 11 – much easier to control the portal themes.

Get SMS2

Go to http://www.wrightccs.com/ and register for your free copy – an email will be sent to you with a download link and your xml based license.

Prepare your environment

You will need SQL/SQLExpress if you don’t already have it (will assume you do). You also need .NET 4 on the RADIUS server (will assume you have that too).

1. On the server you wish to use for RADIUS authentication open server management and click Add Roles and Features

2. Install the Network Policy and Access Services role and add any features that go along with that role – accept all the defaults.
clip_image001
clip_image002

3. Open the Network Policy Server Console

a. Expand Policies and select Network Policies

b. Right click Connections to other access servers and select properties

c. Change it from Deny access to Grant access and hit ok
clip_image003

d. Expand RADIUS Clients and Servers

e. Right click RADIUS Clients and select New

f. Create a connection for the local computer (so you can test connections).
Friendly name – whatever you want to name it
Address – the IP address of the RADIUS server you are creating
Shared secret – type something in that you will remember (will need it later)
Hit OK
clip_image004

g. Do the same thing for your Netscaler(s) using the NSIP(s) – again remember your shared secret – if you have more than one Netscaler use the same shared secret.
Should look something like this when you are done
clip_image005

4. Install SMS2

a. Next

b. For my purposes I select Custom (I don’t want SMS based authentication – just token)

i. Services I set CloudSMS to not install

ii. Under Clients I set all to install but the Citrix Web Interface Customization and SMS2…

c. Configure AuthEngine – enter the license text from the email you received and hit Check License (should pop up when it expires) – click ok and then Next

d. Leave the account as Local System and hit Next

e. On the next screen change the AuthEngine Address to 0.0.0.0 (will reply on all IP addresses of the server)
Type in your domain controller name/address and fill in user account credentials of a user with access to AD
optionally you can change the BaseDN, but I’ll leave it as the root of my test domain
test your config and hit Next if successful
clip_image006

f. Enter your SQL server information
If the SQL instance is on the RADIUS server itself (as it is in my case) check the box to “Use named pipes (local)”
Click Test Connection – I get an error about how it could not use the database… it wasn’t there yet. I hit test connection again and it is successful.
clip_image007

g. Enter your email information – uncheck SSL and Use Auth if you don’t need them (straight smtp for me) – Finish
clip_image008

h. Configure OATHCalc – Next – Finish

i. Configure AdminGUI/Clients – Set the AuthEngine Address to the IP of the RADIUS server, and hit Finish

j. Next – install – Finish

Configure SMS2 for Token

1. Browse to C:\Program Files\WrightCCS2\Settings (assuming you installed the 64bit version… if not the Settings directory will be in x86)

2. Open Configuration.xml in notepad and change these settings (by default they are True, which will mess things up)
<AuthEnginePinCode>False</AuthEnginePinCode>
<AuthEngineChallengeResponse>False</AuthEngineChallengeResponse>

3. Find the <AuthProviders> line

a. Under CloudSMS – disable it (we didn’t install it anyways)
<Enabled>false</Enabled>
<Default>false</Default>

b. Under OATHCalc set it as default
<Default>true</Default>

c. Under PINTAN – disable
<Enabled>false</Enabled>

d. Under Email – disable
<Enabled>false</Enabled>

4. Save the .xml file and restart the WrightAuthEngine service (if they are not started – start them)

Setup all users for token (this could potentially take a long time)

1. Launch the SMS2 Admin Console

2. Select the user on the right hand side to select, and hit Configuration Menu at the top.

3. Go to the Auth Options tab (don’t need the others)
clip_image009

4. Click TOTP (time-based) and click Generate Shared Secret – record the shared secret if you want
clip_image010

5. Click Save configuration and you will see a popup – click OK and then you will see a QR code – copy it to the clipboard and send it to the user (also keep a record of it if you want)
clip_image011

6. Click Close

7. Do that again and again until you have a token for every user who needs to connect to XenApp/XenDesktop through the gateway

At this point users would download Google Authenticator or Microsoft Authenticator (probably others) to their smartphone and add the account using that QR code. Let’s assume everyone has done that.

TEST!!!

1. Download NTRadPing (https://thwack.solarwinds.com/thread/14486) – google it if that link doesn’t work… you will find it

2. From your RADIUS server unzip it and run it (remember we created a client connection for the local computer earlier)
Type the IP of your radius server (port is 1812 if it isn’t there by default)
Leave the reply/retries set to default
Type in your secure string that you associated with the local computer RADIUS client
Type in the domain\username of a user you have configured to use one of the authenticator apps
Type in the password followed immediately with whatever code is showing in your authenticator app. If the password is “P@ssword!” then the password would be P@ssword!456123 (where 456123) is the number generated.
Click Send – If you see Reply-Message=Message accepted then you are good to go. If not then something is wrong.
clip_image012

Configure Netscaler

GUI 10.5

1. Logon your netscaler and browse to Netscaler Gateway\Policies\Authentication\RADIUS

2. Click the Servers tab and click Add
Give it a name
Select Server IP and punch in the IP of the RADIUS server
Port will be 1812
Type in the secret key you used to create the Netscaler RADIUS clients on the RADIUS server
clip_image013
Click Details and set Accounting* to OFF
Click Create

3. Click the Policies tab and click Add
Name the policy
Select the Server you just created (if it isn’t pre-selected)
Type in “ns_true” into the Expression field and hit Create
clip_image014

4. Bind the policy to your Netscaler Gateway virtual server(s) (NetScaler Gateway\Virtual Servers)
Select the virtual server and hit edit
Click the + on Authentication
clip_image016
Choose RADIUS and Secondary from the drop downs and hit Continue
clip_image017
Click to select the policy
clip_image018
Tick the policy you just created and hit ok
clip_image019
Click Bind
clip_image020
Click done and save

At this point you should be ready to test logging onto the gateway page

Testing the gateway

1. Hit your gateway address you will probably notice it has changed and looks something like this:
Password1 is your password
Password2 is your token pin
clip_image021

2. Logon using your credentials and the token generated by Google Authenticator (or whatever app you are using).

a. If it works then you are good to go and can move onto customizing the web interface

b. If it does not work unbind the policy and test to figure out where things are going wrong

i. Could be the wrong IP entered in for the Netscaler on the RADIUS server or wrong security string

Fixing the gateway appearance

1. Download Notepad++ and install (http://notepad-plus-plus.org/download/v6.7.5.html)

2. Download Tunnelier (http://www.bitvise.com/ssh-client-download)

3. Install and run Tunnelier (Bitvise SSH Client)

4. Connect to your netscaler using the password method
clip_image022

5. A command window and a SFTP window will open – select the SFTP window and on the right hand side browse to: /var/netscaler/gui/vpn

6. Select login.js and click the download button at the bottom (will download to your local desktop by default unless you change it on the left side… should be ok).
clip_image023

7. On the right side go into the resources folder and download en.xml

8. Make a backup copy of the files just in case

9. Open login.js using Notepad++
Find this line: if ( pwc == 2 ) { document.write(‘&nbsp;1’); }
and change it to:
if ( pwc == 2 ) { document.write(‘&nbsp;’); }
Just remove the 1 basically
Find the line that starts with: document.write(‘<TR><TD align=right style=”padding-top:
and change right to left

10. Save login.js

11. Open en.xml in Notepad++
Find this line: <String id=”Password2″>Password 2:</String>
Change it to: <String id=”Password2″>Token:</String>
You can name it whatever you want… I’m just using Token:

12. In Tunnelier upload the files to their respective directories overwriting them

13. Refresh your browser and your changes should be reflected.
clip_image024

The only problem now is that this change will not survive a reboot. In older versions of netscaler you could use a rewrite policy to rewrite the page and that would persist. In 10.1+ you have to use a custom theme.

 

Set a custom theme so the gateway appearance persists a reboot

NOTE: Linux is case sensitive… type things exactly as I have them.

1. Using Tunnelier switch to your terminal window
Commands:
shell
cp /nsconfig/ns.conf /nsconfig/ns.conf.save
mkdir /var/ns_gui_custom
cd /netscaler
tar -cvzf /var/ns_gui_custom/customtheme.tar.gz ns_gui/*

What we did there was make a backup of ns.conf (in case something goes awry – reverse the “cp” command to restore it), created a folder, and zipped the contents of /netscaler/ns_gui to /var/ns_gui_custom/customtheme.tar.gz ß that is the file and location that netscaler knows to use for a custom theme.

2. Open your netscaler in your browser, logon and navigate to NetScaler Gateway\Global Settings

3. Click the Change Global Settings link on the right side

4. Click the Client Experience tab and scroll to the bottom

5. Switch the UI Theme to Custom and hit OK
clip_image025

6. TEST the gateway page (I use a chrome incognito window when I make a change as it doesn’t use the cached website)

7. If the test is successful save your netscaler configuration

a. If you have a HA pair I am pretty sure you have to mirror all the steps on the secondary except for setting the UI Theme to Custom. On your secondary:

i. Copy the files to the correct locations on the secondary netscaler

ii. Run the commands from the terminal window

iii. Force a sync from the gui (System\High Availability à Actions)