This is a repost of a blog I did over on MyCUGC.org
When you’re responsible for the care and feeding of your Citrix environment, it is important to keep tabs on your licensing status. In particular, if you have a license type that expires, it is important to monitor that data point to ensure you don’t ever have a “Bad Day™”.
The Citrix licensing documentation explains the different sections of the license files.
Specifically, we’re interested in section 3, “INCREMENT”, with the “CSS_expiry_date” and “exp_date”. The license counts themselves are actually available from the Citrix WMI provider, if you’re interested in monitoring those, at \root\CitrixLicensing.
To monitor these dates in LogicMonitor, we’re going to set up a “PropertySource” to automatically discover servers running the “Citrix Licensing” service, then create a “DataSource”, which will query all licenses in the default path, and alarm on any SA or Expiry dates that are less than 30 days.
To get started, let’s create a “PropertySource” to assign a category “CitrixLicense” to the device.
Settings->PropertySources->Add | PropertySource
Add some descriptive information and “Group” the property source for ease of finding it later.
For the PropertySource Script, paste the following GroovyScript in. Note, you can use PowerShell Scripts to do similar.
import com.santaba.agent.groovyapi.win32.WMI;
def host = hostProps.get(“system.hostname”);
try
{
// get a list of running services
def service_list = WMI.queryAll(host, “select * from win32_service”);
def is_citrixLicenseServer = false;
// enumerate each service as a map
service_list.each
{ service_map ->
// enumerate each of the fields in this service map
service_map.each
{ key, value ->
// is this an CitrixLicense service?
if ((key == “NAME”) && value.contains(“Citrix Licensing”))
{
// yes, flag it
is_citrixLicenseServer = true;
}
}
}
// did we locate an CitrixLicense service?
if (is_citrixLicenseServer)
{
// yes, add the CitrixLicense to system.categories
println “system.categories=CitrixLicense”;
}
return 0
}
catch (Exception e)
{
println e
return 1
}
You can click “test script” to see which devices will be matched. Click “Save” to save the PropertySource.
You can see that if the service is found, we perform add “system.categories=CitrixLicense”. We will use this category in the data source to specify which systems the discovery will apply to.
Alternatively, you can use this Powershell script to apply categories for a variety of common Citrix roles.
$hostname = “##Hostname##”
add-type @”
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
“@
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
$categories=@()
$services=get-service -computername $hostname
foreach($service in $services)
{
switch($service.DisplayName)
{
“Citrix Licensing” { $categories+=”CitrixLicensing” }
“Citrix Broker Service” { $categories+=”CitrixDeliveryController” }
“Citrix PVS Stream Service” { $categories+=”CitrixPVS” }
“Norskale Infrastructure Service” { $categories+=”CitrixWEM” }
“Citrix Remote Broker Provider” { $categories+=”CitrixCloudConnector” }
}
}
$directorResult=Invoke-WebRequest -Uri “https://$hostname/Director” -UseBasicParsing -Method HEAD
if($directorResult.StatusCode -eq 200)
{
$categories+=”CitrixDirector”
}
$storefrontResult=Invoke-WebRequest -Uri “http://$hostname`:8000/StorefrontMonitor/GetSFServicesStatus” -UseBasicParsing
if($storefrontResult.StatusCode -eq 200)
{
$categories+=”CitrixStorefront”
}
write-host “system.categories=$($categories -join “,”)”
exit 0
Next, we will create a DataSource. Settings -> DataSources -> Add DataSource
Fill in the descriptive information.
Relevant settings:
Collect every: 1 day
Collector: Batch Script
Multi-Instance
Enable Active Discovery
Automatically Delete Instance | Delete Immediately
Discovery Schedule: day
Discovery method: Script, Embedded Powershell Script
$hostname = ‘##SYSTEM.HOSTNAME##’
$licenses=@()
$regexPattern=”INCREMENT.*(\d{4}.\d{4}) (\d{1}?-((jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)-2\d{3})|permanent)”
$path=”\\$hostname\C$\Program Files (x86)\Citrix\Licensing\MyFiles”
function Convert-SADateToDateTime{
[Parameter(Mandatory=$true)]
param ($inputString)
$year=$inputString.Substring(0,4)
$mon=$inputString.Substring(5,2)
$day=$inputString.Substring(7,2)
$builtString=”$year/$mon/$day”
$DateTime = Get-Date($builtString)
return $DateTime
}
function Convert-LicenseExpiryDate{
[Parameter(Mandatory=$true)]
param($inputString)
if($inputString -eq “permanent”)
{
$dateString=”Jan-1-2099″
}
else
{
$dateString=$inputString
}
return (get-date $dateString)
}
$files= get-childitem -path $path -Filter *.lic | where-object {$_.name -ne “citrix_startup.lic”}
if ($files)
{
foreach ($file in $files)
{
$currentFileContent=$null
$currentFileContent=get-content $file.pspath
$v=[Regex]::Matches($currentFileContent,$regexPattern)
$SAExpirationDate=Convert-SADateToDateTime $($v[0].groups[1].Value)
$LicenseExpiryDate=Convert-LicenseExpiryDate $($v[0].groups[2].Value)
$licenses+=
[pscustomobject][ordered]@{
FileName = $file.Name
SAExpiration = $SAExpirationDate
LicenseExpiryDate= $LicenseExpiryDate
DaysUntilSAExpiration = (($SAExpirationDate) – (get-date)).days
DaysUntilLicenseExpiration = (($LicenseExpiryDate) – (get-date)).days
}
}
for($i=0; $i -lt $licenses.Length; $i++)
{
write-host “CitrixLicense_$($Licenses[$i].FileName)##$($Licenses[$i].FileName)”
}
exit 0
}
else
{
exit 80009999
}
For the Active Discovery script, we’re supplying a LogicMonitor variable, ##System.Hostname##, for use in connecting from our Collector machine to the Citrix licensing service. Note, in order to do this, the collector service account will need to have UNC access to the remote machine(s).
Assuming you have devices in the “Applies to” category, you should be able to test script (for active discovery) to see which licenses are detected. In my case, the following items were found
The WildValue and WildAlias are specified in the discovery script from this line:
write-host “CitrixLicense_$($Licenses[$i].FileName)##$($Licenses[$i].FileName)”
LogicMonitor parses stdOutput to construct these, per their documentation on Scripted Active Discovery.
At this point, we’re now ready to gather the collector attributes. Paste in this script
$hostname = ‘##HOSTNAME##’
$licenses=@()
$regexPattern=”INCREMENT.*(\d{4}.\d{4}) (\d{1}?-((jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)-2\d{3})|permanent)”
$path=”\\$hostname\C$\Program Files (x86)\Citrix\Licensing\MyFiles”
function Convert-SADateToDateTime{
[Parameter(Mandatory=$true)]
param ($inputString)
$year=$inputString.Substring(0,4)
$mon=$inputString.Substring(5,2)
$day=$inputString.Substring(7,2)
$builtString=”$year/$mon/$day”
$DateTime = Get-Date($builtString)
return $DateTime
}
function Convert-LicenseExpiryDate{
[Parameter(Mandatory=$true)]
param($inputString)
if($inputString -eq “permanent”)
{
$dateString=”Jan-1-2099″
}
else
{
$dateString=$inputString
}
return (get-date $dateString)
}
$files= get-childitem -path $path | where-object { $_.name -ne “CITRIX.opt” -and $_.name -ne “citrix_startup.lic” }
foreach ($file in $files)
{
$currentFileContent=$null
$currentFileContent=get-content $file.pspath
$v=[Regex]::Matches($currentFileContent,$regexPattern)
$SAExpirationDate=Convert-SADateToDateTime $($v[0].groups[1].Value)
$LicenseExpiryDate=Convert-LicenseExpiryDate $($v[0].groups[2].Value)
$licenses+=
[pscustomobject][ordered]@{
FileName = $file.Name
SAExpiration = $SAExpirationDate
LicenseExpiryDate= $LicenseExpiryDate
DaysUntilSAExpiration = (($SAExpirationDate) – (get-date)).days
DaysUntilLicenseExpiration = (($LicenseExpiryDate) – (get-date)).days
}
}
foreach($license in $licenses)
{
“CitrixLicense_$($license.Filename)`.DaysUntilSAExpiration=$($license.DaysUntilSAExpiration)”
“CitrixLicense_$($license.Filename)`.DaysUntilLicenseExpiration=$($license.DaysUntilLicenseExpiration)”
}
exit 0
This script will retrieve all licenses on the specified host, then for each license discovered, it uses a regex pattern to extract the dates. If the word “permanent” is found, we convert that to January 1, 2099. Since the SA expiry date isn’t a format that “get-date” understands, we convert it to a format that will work for the cmdlet.
Finally, for each discovered license, we output text to stdOutput in the key=value format that LogicMonitor will parse.
CitrixLicense_FID_19445f95_d1a0_4594_81df_e871e87a4678.lic.DaysUntilSAExpiration=356
CitrixLicense_FID_19445f95_d1a0_4594_81df_e871e87a4678.lic.DaysUntilLicenseExpiration=386
CitrixLicense_FID__23f6bf6d_161c87582c5__26bf.lic.DaysUntilSAExpiration=-42
CitrixLicense_FID__23f6bf6d_161c87582c5__26bf.lic.DaysUntilLicenseExpiration=-12
Finally, we create the actual datapoints from the output of the collection. Click “Add DataPoint” and create 2 gauge datapoints.
Set the alert threshold
Then click save.
Once you’ve saved your datapoint, any matching devices will run active discovery and you should now see your licenses associated with that device! Based on your notification/escalation settings within LogicMonitor, you now have actionable monitoring of a vital piece of Citrix infrastructure.