Citrix Monitoring ODATA API

Function Get-LocalTime($UTCTime)
$strCurrentTimeZone = "Eastern Standard Time"
$TZ = [System.TimeZoneInfo]::FindSystemTimeZoneById($strCurrentTimeZone)
$LocalTime = [System.TimeZoneInfo]::ConvertTimeFromUtc($UTCTime, $TZ)
Return $LocalTime

$datescope = (get-date).AddHours(-3).ToUniversalTime()
$datescope = get-date($datescope) -Format s
$filename = "SessionActivitySummariesByMinute-" + (get-date ($datescope) -f "MM-yyyy") + ".csv"
$uri = "http://localhost/Citrix/Monitor/OData/v3/Data/SessionActivitySummaries()?`$filter=Granularity eq 1 and SummaryDate gt DateTime`'$datescope`' &`$select=DesktopGroup/Name,SummaryDate,ConcurrentSessionCount,ConnectedSessionCount,DisconnectedSessionCount&`$orderby=SummaryDate asc&`$expand=DesktopGroup&`$format=json"
$records = Invoke-RestMethod -Uri $uri -UseDefaultCredentials
$records.value | Select-Object @{Name='SummaryDateEST';Expression={Get-LocalTime($_.SummaryDate)}},ConcurrentSessionCount,ConnectedSessionCount,DisconnectedSessionCount,@{Name='DesktopGroupName';Expression={$_.DesktopGroup.Name}} | Export-csv .\$filename -NoTypeInformation -Append

Citrix provides access to Monitoring Data via ODATA API, I find it useful to extract session info to a very granular level [eg: by mintue]. This is the same data Citrix Director uses to present the fancy usage graphs but as you expand to longer time series it averages it and you might not get the accurate insights for capacity planning. The PS code shown above extracts and exports the SessionActivitySummary by minute to a csv file. the reason for the export is Citrix purges the ByMinute data, by default it only retains it for last 3 days. you could override this default retention value using Set-MonitoringConfiguration . I find it easy to export and generate graphs using pivot tables in excel.

The PS script above could you used to set up as scheduled tasks on a controller and set to run every 3 hours. This provides a way to archive the data as well as leaving the default retention values in Citrix.

Download code from

DUO ADMIN API Functions through PowerShell

Forked from Duo-PSModule by mbegan, added new Administrator Activation Link functions. this automates the provisioning process to the duo admin console and lets you create the account with just corp email whereas GUI forces you to enter temp password and require to key in the user’s phone #.

Extract MFA/StrongAuth information from all Azure/O365 users

MSOnline PowerShell module is required to run this, the new AzureAD commandlets do not appear to have the strong authentication properties yet. Run the following PowerShell lines to load and connect to your Azure/o365 tenant.

Install-Module -Name MSOnline 

Powershell snippet below gets all user from the tenant and expands StrongAuthenticationUserDetails property to retrieve the enrolled MFA info and further extends to extract default MFA method using PowerShell expression and saves it to c:\tmp\Azure-2FAEnrollmentReport.csv, using PS expression we were able to expand the second property in a single line.

Get-MsolUser -All | select userprincipalname, DisplayName,title, Department -ExpandProperty StrongAuthenticationUserDetails | select UserPrincipalName,DisplayName,Title,Department,AlternativePhoneNumber,PhoneNumber, @{ Name = 'default2FAMethodType'; Expression = {  (Get-MsolUser -UserPrincipalName $_.UserPrincipalName  | select -ExpandProperty StrongAuthenticationMethods | where {$_.IsDefault}).MethodType }} | Export-csv c:\tmp\Azure-2FAEnrollmentReport.csv

A quick way to generate self-signed certs through PowerShell

code snippet to generate a 10-year SSL self-signed cert, notice the NotAfter argument.

New-SelfSignedCertificate -DnsName "i7host" -CertStoreLocation "cert:\LocalMachine\My" -KeyAlgorithm RSA -KeyLength 2048 -NotAfter (Get-Date).AddYears(10)

Once successfully run, cert can be exported through local machine certificate MMC. New-SelfSignedCertificate command-let is available in windows 8.1 and above.

Force specific IP traffic through a network interface [Windows 8+]

In a case where you have two network interfaces, eg: 4G data card and local ethernet card connect to your device and you would like traffic to a specific destination to go via a preferred network interface, PS code below could guide you through it.

E.g ps code below shows the route to to go through interface 4, this is done by setting a lower route metric than the other interface card.

# you can determine your adapters with Get-NetAdapter
Get-NetAdapter -IncludeHidden
# then you can see what routes are associated with what adapter interface (lets assume your wifi interface is 4 and your loopback is 1)
Get-NetRoute -AddressFamily IPv4
# you will get your specific interface index, destination prefix, nexthop and the routemetric
# you can then set a specific route policy using:
New-NetRoute -DestinationPrefix "" -InterfaceIndex 1 -RouteMetric 256
New-NetRoute -DestinationPrefix "" -InterfaceIndex 4 -NextHop10.1.1.1 -RouteMetric 0
# you can modify the configuration with:
Set-NetRoute -DestinationPrefix "" -InterfaceIndex 4 -NextHop192.168.10.1 -RouteMetric 0
# finally, you can remove the specific route or all the routes with:
Remove-NetRoute -DestinationPrefix "" -InterfaceIndex 1 -Confirm:$false
Remove-NetRoute -DestinationPrefix "" -Confirm:$false

Credit to Brandon Records

Handle Netscaler AAA > "Target URL not found for redirection" after login

Citrix published a solution for this [CTX224908]; saying hit the LB first and have it populate “NSC_TASS” cookie. That might not work for all audiences.
Users tend to bookmark the login page. Which happens to be AAA page. When they go back to their bookmark they hit the AAA page directly with out the “NSC_TASS” cookie to redirect the user after successful authentication. Hence they see “Target URL not found for redirection”
In this post, I will go over how I handled this.
Prereq: NetScaler version 11.0 build 64.34 or later. [ref > CTX201949]
Setup: LB VIP and AAA VIP behind Content Switch [single URL]
Build CS VIP, LB VIP and AAA VIP per CTX201949; Add global responder policy to automatically redirect to hostname when NSC_TASS does not exist in the HTTP REQ

add responder policy res_pol_redirect_hostname "HTTP.REQ.URL.PATH_AND_QUERY.CONTAINS(\"/vpn/tmindex.html\") && http.REQ.HEADER(\"Cookie\").CONTAINS(\"NSC_TASS\").NOT" res_redirect_hostname -comment "handle no target resource after AAA Auth"
add responder action res_redirect_hostname redirect "\"https://\" + http.REQ.HOSTNAME" -responseStatusCode 302
bind responder global res_pol_redirect_hostname 100 END -type REQ_DEFAULT

Extract HDX/ICA Connection info from Citrix Monitoring Database

Follow OData Connection to Citrix Delivery Controller to create a connection to Citrix monitoring data.
for XenDestkop version 7.0 – 7.5 use http://{ddc-host}/Citrix/Monitor/OData/v1/Data
for XenDestkop version 7.6 and 7.7 use http://{ddc-host}/Citrix/Monitor/OData/v2/Data
for XenDestkop 7.8 and above use http://{ddc-host}/Citrix/Monitor/OData/v3/Data

/*Report HDX/ICA connection information for User: mulpurus and DesktopGroup: Win10-Standard for the May 31 - Jun 17
 Extract UserFullName, DesktopGroupName, StartTime, EndTime, ClientName and Client IP Address */
//Start Date
String Date = "05/31/2017";
//Converting string to date
DateTime StartDate = Convert.ToDateTime(Date);
//EndDate - Adding 18 days to startdate
DateTime EndDate = StartDate.AddDays(18);
Console.WriteLine("Report Generated From {0} to {1}",StartDate,EndDate);
//LINQ query returns UTC, will be using this TimeZoneInfo estZone obj to convert to EST.
//Tweak this to your desired Timezone
TimeZoneInfo estZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
//Connection varible to hold the query result, for each session
var Connection = from C in Sessions
 //filters - Start Date and EndDate, UserName and DeliveryGroup
 where (C.StartDate >= StartDate && C.StartDate < EndDate && C.User.UserName == "mulpurus" && C.Machine.DesktopGroup.Name == "Win10-Standard" && C.EndDate != null)
 //Sort by StartDate
 orderby C.StartDate
 //Extract UserName, FullName, DesktopGroup, StartDateTime, EndDateTime, ClientName, ClientAddress
 select new {C.User.UserName,C.User.FullName, DesktopGroup = C.Machine.DesktopGroup.Name,StartDateTime = TimeZoneInfo.ConvertTimeFromUtc(C.StartDate.Value, estZone), EndDateTime = TimeZoneInfo.ConvertTimeFromUtc(C.EndDate.Value, estZone), C.CurrentConnection.ClientName,C.CurrentConnection.ClientAddress };
//Display the query result




  1. Usage trends
  2. Capacity planning
  3. Auditing

Citrix PVS Server Tweaks

  • Streaming Port re-configured from 6910 to 6968 (default 6910 – 6930).
  • Threads per port set to match the vCPU number.
    VM level set virtual sockets to vCPU number and the cores per socket to one.
  • Leave the rest advanced options to be unchanged.