Virtualization, technology, and random rantings with a focus on Citrix and VMware.

Category: Citrix Page 4 of 10

You Got Some Of That HSD? : Hosted Shared Desktop Information Gathering Script

Cropped image to remove space from user list

There are times that you created some Hosted Shared Desktops. You published them and gave them out. You want to make sure you don’t duplicate your work and create another one when you may have one that is already available for a user group. How about a script to see what you got, what you named it, what Delivery Group is hosting it, what users are assigned to it, and what servers are assigned to it? I’ve got just the script for you!

# Script to get all Hosted Shared Desktops and user assignments using ISE 5.1 and Citrix Studio SDK installed locally.

asnp Citrix*

$date = Get-Date -Format MMddyyyy

$adminAddress = "ddc.fqdn:80"

$getHSD = Get-BrokerEntitlementPolicyRule -AdminAddress $adminAddress -MaxRecordCount 10000

$report = @()

foreach($hsd in $getHSD){
  $line                  = "" | Select-Object PublishedName, IncludedUsers, DeliveryGroup, ServerNames 
  $desktopGroup          = Get-BrokerDesktopGroup -AdminAddress $adminAddress -uid $hsd.DesktopGroupUid
  $machineNames          = Get-BrokerMachine -AdminAddress $adminAddress -DesktopGroupName $desktopGroup.Name
  
  $line.PublishedName    = $hsd.PublishedName
    
  if($hsd.IncludedUsers.Name -ne $null){
    
    $line.IncludedUsers  = ($hsd.IncludedUsers.Name -join ';').ToString()
    
  }
  
  elseif($hsd.IncludedUsers.Name -eq $null){
   
    $line.IncludedUsers  = "None Assigned"
    
  }
  
  if($desktopGroup.Name -ne $null){
   
    $line.DeliveryGroup  = $desktopGroup.Name -join ';'
    
  }
  
  elseif($desktopGroup.Name -eq $null){

    $line.DeliveryGroup  = "None Assigned"
    
  }
  
  if($machineNames.HostedMachineName -ne $null){
    
    $line.ServerNames    = $machineNames.HostedMachineName -join ';'
    
  }
  
  elseif($machineNames.HostedMachineName -eq $null){

    $line.ServerNames    = "None Assigned"

  }
  
  $report += $line

}

$report | Export-CSV c:\scripts\logs\$date-hostedshareddesktops.csv -Append -NoTypeInformation

Have You Tried A Good Old Fashioned Reboot? : Script To Get Citrix Delivery Group Reboot Schedules

Maybe when you are planning to do some upgrades but don’t remember when you set your reboot schedule times on your Delivery Groups. This will show you all the reboot schedules you have configured with server names. This also shows if you have Delivery Groups with no servers assigned to them, but had previously created a reboot schedule.

Update: I did run into an interesting thing with it. I had to define the if and elseif in order for it to evaluate as being true. Not sure what was going on with that.

# Script to get reboot schedules of Delivery Groups with times and server names using ISE 5.1 and Citrix Studio SDK installed locally.

asnp Citrix*

$date = Get-Date -Format MMddyyyy

$adminAddress = "ddc.fqdn:80"

$ctxRebootSchedule = Get-BrokerRebootScheduleV2 -AdminAddress $adminAddress -MaxRecordCount 100000

$report = @()

foreach($ctx in $ctxRebootSchedule) {
  $line                  = "" | Select-Object DesktopGroupName, ScheduleName, Enabled, Frequency, StartTime, RebootDuration, ServerCount, ServerNames
  $machineNames          = Get-BrokerMachine -AdminAddress $adminAddress -DesktopGroupName $ctx.DesktopGroupName
    
  $line.DesktopGroupName = $ctx.DesktopGroupName
  $line.ScheduleName     = $ctx.Name
  $line.Enabled          = $ctx.Enabled
  $line.Frequency        = $ctx.Frequency
  $line.StartTime        = $ctx.StartTime
  $line.RebootDuration   = $ctx.RebootDuration
  $line.ServerCount      = ($machineNames.HostedMachineName).count 
  
  if(($line.ServerCount) -ne 0){
   
    $line.ServerNames    = ($machineNames.HostedMachineName) -join ';'  
    
  }
  elseif(($line.ServerCount) -eq 0) {
    
    $line.ServerNames    = "None Assigned To DG"
    
  }
  
  $report += $line
  
  }
  
$report | export-csv c:\scripts\logs\$date-Citrix-Reboot-Schedule.csv -Append -NoTypeInformation

It Just Didn’t Register: Find Unregistered Machines

Just a quick little script you can add to your daily checks. This has been helpful for me to see if I have something that didn’t want to play nice BEFORE someone calls me and says it is broken. Good to have also if you have multiple hypervisor connections so you can see where at least it is running. Saves you looking around to figure out where it be.

# Get unregistered machine information

$adminAddress = "ddcaddress.fqdn:80"

$unregisteredMachines = get-brokermachine -AdminAddress $adminAddress -MaxRecordCount 25000| where registrationstate -eq "unregistered"
$report = @()
    foreach($unreg in $unregisteredMachines){

      $line = "" | select HostedMachineName, RegistrationState, AssociatedUserNames, DesktopGroupName, CatalogName, InMaintenanceMode, OSType, LastConnectionUser, LastConnectionTime, HypervisorConnectionName, SessionCount

      $line.HostedMachineName        = $unreg.HostedMachineName
      $line.RegistrationState        = $unreg.RegistrationState
      $line.AssociatedUserNames      = $unreg.AssociatedUserNames -join ','
      $line.DesktopGroupName         = $unreg.DesktopGroupName
      $line.CatalogName              = $unreg.CatalogName
      $line.InMaintenanceMode        = $unreg.InMaintenanceMode
      $line.OSType                   = $unreg.OSType
      $line.LastConnectionUser       = $unreg.LastConnectionUser
      $line.LastConnectionTime       = $unreg.LastConnectionTime
      $line.HypervisorConnectionName = $unreg.HypervisorConnectionName
      $line.SessionCount             = $unreg.SessionCount

      $report += $line

    }

    $report |format-table
    
    

You Are On The Not Allowed List! Get That Outta Here! : Geo-IP Blocking With Responder Policy

Update coming for a process to do a weekly update of the CSV files from MaxMind to use more current files than the included ones in the ADC firmware.

So you have some compliance you need to meet on your IRS1075. You might see one of those points that you have to restrict based on origin country IP. Well… That can be a little bit of fun (note below on how that can be fun). So you will need to logon to your ADC with your super secret squirrel account to change the laws of access. For this, I would recommend using the all powerful command line. There is a link to a CTX article that explains parts of this https://support.citrix.com/article/CTX130701/how-to-use-netscaler-to-block-access-to-a-website-using-a-location-database-based-on-users-country

In this example, I’m using Ubuntu on WSL for that Windows / Linux immersive experience.

Login to ADC with SSH.

This is using the inbuilt csv that is part of the firmware that is in the ADC from firmware 11.0. You can use an external file as well which is outlined in the CTX130701 article.

Enter in “add locationFile “/var/netscaler/inbuilt_db/Citrix_Netscaler_InBuilt_GeoIP_DB.csv””

Hit enter and it will report “Done.”

Enter in “add audit messageaction log_locationInformation NOTICE “\”dropped request for \” + CLIENT.IP.SRC + \” from \” + CLIENT.IP.SRC.LOCATION” -logtoNewnslog YES”

This will create the audit log that you can associate to the Responder Policy to write out to whatever syslog you are using.

Enter “add responder policy Drop_non_US_IRS1075 “CLIENT.IP.SRC.MATCHES_LOCATION(\”.US....\”).NOT && CLIENT.IP.SRC.MATCHES_LOCATION(\”.CA....\”).NOT && CLIENT.IP.SRC.IN_SUBNET(10.0.0.0/8).NOT && CLIENT.IP.SRC.IN_SUBNET(172.16.0.0/12).NOT && CLIENT.IP.SRC.IN_SUBNET(192.168.0.0/16).NOT” DROP -logAction log_locationInformation”

This will allow all USA and Canada IPs, and the private addresses for the internal users. If you leave that part off, well…. they aren’t gonna connect internally. This is assuming you want to allow all the private internal IP ranges. You can narrow that further if you wish to be more restrictive. **this is note referenced above**

Enter “set locationParameter -matchWildcardtoany YES” This is for NetScaler 11.1 build 53.11 and above. This is referenced in the CTX article.

Enter “show locationparameter” and it will show the loaded csv file and the “Match wildcard qualifier to any: YES”

You have the audit log configured and the Responder Policy configured. You will need to now bind it to your vservers to apply the policy.

Enter “bind lb vserver <vservername> -policyName Drop_non_US_IRS1075 -priority 100”

I would definitely recommend testing this on an internal testing site so as not to break your production environment. Happy restricting!

VDA Upgrade… Oh Yeah!

So you want to upgrade some VDAs?! Yeah you do! I’ve done some edits on other scripts. I’m also working out for additional revisions to check for present sessions. This targets the Server VDA version. You can edit the name to VDAWorkstationSetup_1912.exe in the script and accompanying files to upgrade on VDI as well. The base for this is listed below in the script from ChayScripts. You can also change the install switches and copy that into the install.bat file if you need different options (https://www.citrix.com/blogs/2018/01/08/citrix-vda-commandline-helper-tool/)

For the contents of the ServerNameTextFile, you will need FQDN of the servers for the invoke commands. This splits the FQDN off for the powercli aspect to snapshot the server.

# Base VDA removal / reinstall script. This uses Powershell ISE on 5.1 with needing to be ran with account with rights in VMware and on the target server as well as the module for PowerCLI. 
# Modified from https://github.com/ChayScripts/Citrix-VDA-Upgrade-Scripts scripts. Added snapshot for VMware and a report of previous versions.

$vdalist = get-content "C:\pathtotextfilewithfqdnservernames.txt"
$source = "placewherefilesarestored\vdaupgrade"
$dest = "c$\software\vdaupgrade"
$date = Get-Date -Format MMddyyyy
$report = @()

  foreach ($vda in $vdalist) {
    $line = "" | Select Name, PreviousVersion, SnapShot
    $vda1 = ($vda.split('.')[0])
    $line.Name = "$vda"
    $line.PreviousVersion = (invoke-command -ComputerName $vda -ScriptBlock {Get-WmiObject -Class Win32_Product | where name -match "Citrix Virtual Desktop Agent - x64" | select Name,Version}).Version
    $snapshot = (get-vm $vda1  | new-snapshot -name $date-$vda1-preupgrade)
    $line.SnapShot = (get-vm $vda1 | get-snapshot).name
    Write-Host "Working on $vda"
    if (!(Test-Path -Path \\$vda\c$\software\vdaupgrade)) {
        New-Item -ItemType Directory -Path \\$vda\c$\software -Name vdaupgrade
        Copy-Item "\\$source\install.bat" -Destination \\$vda\$dest -Force
        Copy-Item "\\$source\remove.bat" -Destination \\$vda\$dest -Force
        Copy-Item "\\$source\VDAServerSetup_1912.exe" -Destination \\$vda\$dest -Force
    }
    else {
        Copy-Item "\\$source\install.bat" -Destination \\$vda\$dest -Force
        Copy-Item "\\$source\remove.bat" -Destination \\$vda\$dest -Force
        Copy-Item "\\$source\VDAServerSetup_1912.exe" -Destination \\$vda\$dest -Force

    }
    Invoke-Command -ComputerName $vda -Scriptblock {
        $time = (Get-Date).AddMinutes(3)
        $action = New-ScheduledTaskAction -Execute 'c:\software\vdaupgrade\remove.bat'
        $trigger = New-ScheduledTaskTrigger -Once -At $time
        $principal = New-ScheduledTaskPrincipal  -RunLevel Highest -UserID "NT AUTHORITY\SYSTEM" -LogonType S4U

        Register-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -TaskName "VDAUninstall" -Description "Citrix VDA Uninstall" 
    }

    Invoke-Command -ComputerName $vda -Scriptblock {
        $action = New-ScheduledTaskAction -Execute 'c:\software\vdaupgrade\install.bat'
        $trigger = New-ScheduledTaskTrigger -AtStartup 
        $principal = New-ScheduledTaskPrincipal  -RunLevel Highest -UserID "NT AUTHORITY\SYSTEM" -LogonType S4U

        Register-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -TaskName "VDAInstall" -Description "Citrix VDA Install" 

    } 

    $report += $line

  }

$report | export-csv c:\scripts\logs\$date-vda-upgrades.csv -Append -NoTypeInformation

You will need to create an install.bat and remove.bat file with the contents below.

Install.bat
REM change port number in below command.
REM Use citrix vda command line helper tool from citrix. https://support.citrix.com/article/CTX234824 if needed
REM Install new VDA agent, delete files and scheduled tasks. Finally reboot.

C:\software\vdaupgrade\VDAServerSetup_1912.exe /masterpvsimage /virtualmachine /components VDA /controllers "DDC1 DDC2 DDC3" /noreboot /quiet /disableexperiencemetrics /enable_hdx_ports /enable_hdx_udp_ports /enable_real_time_transport /enable_remote_assistance
C:\Windows\system32\schtasks.exe /delete /tn VDAInstall /f
C:\Windows\system32\schtasks.exe /delete /tn VDAUninstall /f
del c:\software\vdaupgrade\remove.bat /F
del c:\software\vdaupgrade\VDAServerSetup_1912.exe /F
C:\Windows\System32\timeout.exe /t 5
C:\Windows\System32\shutdown.exe /r /t 20 /f
del c:\software\vdaupgrade\install.bat /F

Remove.bat
"C:\Program Files\Citrix\XenDesktopVdaSetup\XenDesktopVdaSetup.exe" /REMOVEALL /QUIET /NOREBOOT
C:\Windows\System32\shutdown.exe /r /t 5 /f

User Count Breakdown By Delivery Group

This script allows you to get the list of Delivery Groups by “SessionSupport” type and reports back “MultiSession” as “CitrixApp” and SingleSession as “VDI.”

# Script to get MultiSession and SingleSession counts from Delivery Groups with a non-zero user count. This sorts by MultiSession, then SingleSession. This was ran on a machine with Citrix Studio SDK
# installed. This was tested with CVAD 1912 LTSR.
asnp Citrix*
$adminAddress = "deliverycontroller.fqdn"

$getDG = Get-BrokerDesktopGroup -AdminAddress $adminAddress -MaxRecordCount 100000 | Select-Object Name, SessionSupport | Get-Unique -AsString

$report = @()

foreach($dg in $getDG) {
  
  $line = "" | Select DeliveryGroupName, UserCount, SessionSupport
  $userCount = (Get-BrokerSession -AdminAddress $adminAddress -DesktopGroupName $dg.name -MaxRecordCount 100000 | Select-Object BrokeringUserName).count
  
  if ($userCount -ne '0' -and $userCount -ne $null){
    $line.DeliveryGroupName = $dg.name
    $line.UserCount         = $userCount
    
    if($dg.SessionSupport -eq "SingleSession") {
      $line.SessionSupport  = 'VDI'
    }
    else{
      $line.SessionSupport  = 'CitrixApp'
    }
    $report += $line
  }
  }
  
$citrixAppTotal = (($report | Where-Object SessionSupport -eq "CitrixApp"| Select-Object UserCount).UserCount| Measure-Object -Sum).Sum
$citrixVDITotal = (($report | Where-Object SessionSupport -eq "VDI"| Select-Object UserCount).UserCount| Measure-Object -Sum).Sum
$appTotal = write-output "`r`nTotal Citrix App users: $citrixAppTotal"
$vdiTotal = write-output "Total VDI Users: $citrixVDITotal"
  
$report += $apptotal
$report += $vdiTotal
  

$report | sort SessionSupport, @{Expression="UserCount";Descending=$true}|Format-Table




Good Ole Proxy Top, Forward Style

So you want to get that sweet, sweet forward proxy all up there for some kiosks? Well… Have I got a deal for you! If you happen to have the licensing (Premium license requirement), you too can be the proud owner of this actually wonderful product. I have been using this for years now and it works extremely well if you don’t have to constantly add sites to the allowed list. Now… First things first. This is for defining your OWN allow list that YOU have to maintain. Getting Gmail to work will take some effort as there are a LOT of sites you have to add for images. This is not using the URL Threat Intelligence which is a line item purchase. This was completed with the help of Kevin Lofy from Citrix. https://www.linkedin.com/in/jkevinlofy/. This also is the GUI way of configuring this. Hope that this is of help. It REALLY solved a couple issues and allowed a good bit of control with using AppLocker on the VDA hosting server that was publishing the Firefox browser that linked to the proxy address.

Logon to Citrix ADC.

You will need to select “System” > “Settings” > “Configure Basic Features.”

If “Integrated Caching” is not enabled, you will need to enable the feature. This WILL require a reboot.

Select “Settings” > “Configure Advanced Features.”

You will need to select “SSL Interception” and “Forward Proxy.”

Navigate to “Traffic Management” > “DNS” > “Name Servers” and select “Add.”

Select “IP Address.”

Enter “IP Address.”

Select “UDP” from “Protocol.”

Click “Create.”

Navigate to “Security” > “SSL Forward Proxy.”

Select “Certificate Bundles.”

Select “SSL Forward Proxy Wizard.”

Click “Get Started.”

Click “Continue.”

Enter “Name” for Proxy.

Select “Explicit” from “Capture Mode.”

Click “Continue.”

Click “Continue.”

Select “SSL Sessions Interception.”

Select “Add.”

Select “Bind.”

Select “Add.”

I used an Ubuntu machine and hosted the text file there and reference it as http://ip/something.txt

This will set it to go to next line.

Click “OK.”

HTTP.REQ.HOSTNAME.APPEND(HTTP.REQ.URL).URLSET_MATCHES_ANY(“urlsetname”) || HTTP.REQ.URLSET_MATCHES_ANY(“urlsetname”)

Click “Close.”

Click “OK.”

You will need to set a policy to set the IP and Port defined for the proxy (typically 8080) and apply to the machine that will be using the proxy. Using AppLocker with it will make it harder to pivot out of for machine security.

I’ll gather up a blog post of AppLocker and a way to use it with SSL Forward Proxy.

Windows Terminal On Citrix VDI Keyboard No Worky

So ran into this fun on Citrix VDI with Windows Terminal. You get it installed. You start it up. It’s all shiny. You press a button….. And…… NOTHING! So we saw this issue on Windows Terminal on Windows 10 20H2 running CVAD 1912 CU5 VDA. A little bit of searching and this article pointed to part of what was up. https://github.com/microsoft/terminal/issues/4448

The fix that had to be done to resolve it in our case was to set the “Touch Keyboard and Handwriting Panel Service” to “Manual” in Services. Then rebooting. After that, it fired right up and worked!

If You Could Get Those User Counts Today, That Would Be Great.

So you like your reports fresh off the press!? We do too! A quick little script to grab a daily report, running as a scheduled task, and send out some user information. Just in case someone likes to know how many people using the platform.

Gets you this nice little emailed report:

# Get Citrix Daily Users reporting. This requires Powershell, Studio SDK, access rights to the license server.
# This gets the unique users, current sessions, connected and disconnected VDI, and license counts.
# This was tested with 11.17.2.0 build 37000 License Server with 1 license file. Running this as a scheduled task you will need an AD account
# to run this under. A service account works well for this.

asnp Citrix*

$adminAddress = "deliverycontroller.fqdn:80"

$licenseServerAddress = "https://licenseserveraddress:8083"

$licenseServerName = "licenseserver.fqdn"

$cert = Get-LicCertificate -AdminAddress $licenseServerAddress

# This section was gotten from https://lalmohan.co.nz/2015/10/09/citrix-license-usage-monitoring-using-powershell/ and modified for my use.
$licenseInfo = Get-WmiObject -Namespace "ROOT\CitrixLicensing" Citrix_GT_License_Pool -ComputerName $licenseServerName
$licenseModel = ($LicenseInfo | Where-Object{($_.pld -like "XDT*") -or ($_.pld -like "MPS*")}|Select-Object pld -unique).pld
$totalLicenses = ($licenseInfo | Where-Object PLD -like "$licenseModel" | Select-Object count).count
# End section.

# This section was assisted from http://notesofascripter.com and https://www.linkedin.com/in/douglas-ruehrwein-56835869/
# This will run differently for Monday since you are getting data from the last 24 hours and weekends are usually lower use.
$Today = Get-Date
if(($Today.DayOfWeek) -eq 'Monday')
{$when = $Today.AddDays(-3)}
else{$when = $Today.AddDays(-1)}
# End section.

$connections = Get-BrokerConnectionLog -AdminAddress $adminAddress -Filter {BrokeringTime -gt $when} -MaxRecordCount 100000 | Select-Object BrokeringUserName

$licenseCount = (Get-LicUsageDetails -AdminAddress $licenseServerAddress -ProductEditionModel $licenseModel -CertHash $cert.CertHash).count


$ctxUsers = [PSCustomObject] @{

  UniqueCitrixUsers      = ($connections.BrokeringUserName | Select-Object -Unique).count
  CurrentSessions        = (Get-BrokerSession -AdminAddress $adminAddress -MaxRecordCount 100000 | Select-Object BrokeringUserName).count
  CitrixVDIConnected     = (Get-BrokerSession -AdminAddress $adminAddress -MaxRecordCount 100000 | Where-Object SessionSupport -eq "SingleSession" | Where-Object SessionState -eq "Active").count
  CitrixVDIDisconnected  = (Get-BrokerSession -AdminAddress $adminAddress -MaxRecordCount 100000 | Where-Object SessionSupport -eq "SingleSession" | Where-Object SessionState -eq "Disconnected").count
  CitrixLicensesUsed     = $licenseCount
  CitrixTotalLicenses    = $totalLicenses
  CtxLicenseFreePercent  = ((($totalLicenses - $licenseCount) / $totalLicenses ) * 100).ToString("#.##")

}

# HTML Formatting
$style = "<style>BODY{font-family: Arial; font-size: 10pt;}"
$style = $style + "TABLE{border: 1px solid black; border-collapse: collapse;}"
$style = $style + "TH{border: 1px solid black; background: #dddddd; padding: 5px; }"
$style = $style + "TD{border: 1px solid black; padding: 5px; }"
$style = $style + "</style>"

# HTML Email Body
$body = $ctxUsers | ConvertTo-Html -Head $style
 

# Generates email with attachment
$date = Get-date -Format "MM-dd-yyyy"
$emailFrom = "someemail@place.com"
$emailto = "someemail@place.com"
#$emailtwo = "someemail@place.com"
#$emailCC = "someemail@place.com"
$subject="Daily Citrix User Report | $date" 
$email = New-object System.Net.Mail.MailMessage 
$email.to.Add($emailto)
#$email.to.Add($emailtwo)
#$email.CC.Add($emailCC)
$Email.From = New-Object system.net.Mail.MailAddress $emailFrom
$email.Subject = $subject
$email.IsBodyHtml = $true
#$attachment = $Reports[1]
#$email.Attachments.add($attachment)
$email.body = $body
 

$smtpserver="stmp.someplace.com" 
$smtp = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($email)





That One Time, You Got SMAPP’d!

So you run SiteManager. And somebody done decided they want to make a new server that will host the security.dat file. And… You already did the work to create custom .ini file locations for the users. NOW you have to change all those smapp.ini files with the updated location of the security.dat file. How dare they?! Well. That could be some fun if you have a lot of users. Wait…. Powershell for the rescue! If you happen to use a profile server to host the user files, you can easily replace it with the new location of the security.dat file.

Update: Not sure what happened, but the code paste didn’t take evidently. I blame gremlins. It has been corrected.

# Replace a line / value in .ini file stored in Citrix UPM folder location when a change to the application is made.
# An example is for SiteManager, if you change the location of the .dat file for security.dat file and you are using a custom .ini
# created and stored with the user profile.

$filePath = "e:\locationofupmfolders"

$Files = Get-ChildItem -Path $filePath -Recurse -File -force -Include "smapp.ini"

foreach($file in $files)
    {
        $find = "value-you-want-to-change"
        
        $replace = "value-you-want-to-change-to"
        
        $content = Get-Content $($file.FullName) -Raw
        
        #write replaced content back to the file
        $content -replace $find,$replace | Out-File $($file.FullName) | write-output
        
        
    }  
 

Easy peasy. Now they have the new location of the security.dat file!

Page 4 of 10

Powered by WordPress & Theme by Anders Norén