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

Category: XenApp Page 3 of 7

Moving Control Plane To Cloud: Migrating Citrix Daily User Report

Third in the series of moving the control plane to Citrix Cloud…. So you had your daily user report kicking out everyday (Surely you created one from this other post: https://xenapplepie.com/2022/04/12/if-you-could-get-those-user-counts-today-that-would-be-great/). It was working its happy way through life. Then you just moved parts it talked to into the cloud. I have created this updated report script to allow for it to pull from Citrix Cloud. This requires that you have already setup your API access with the secureclient.csv, that you added the CustomerID to your secureclient.csv, and you have installed the Citrix Cloud SDK. If you don’t have those, you are gonna have a bad day. I left the comment for the #Get Licensing Info so you can see what all other fields you can get if needed from there. If you are using VS Code, when you run that section, you can create a new variable and assign it as “$content.” and it will show the other available pieces of information you can assign such as “deviceLicenseUsage.”

**Update: Removed line with Get-XDAuthentication as it is doing a double authentication. Changed SDK commands to use $headers.Authorization to pass same bearer token**

Example Of Autocomplete From VS Code For Licensing
Sample Output From Script Email
# Citrix Daily Report with updates for using Citrix Cloud. This was done in Powershell ISE 5.1 with Citrix Cloud SDK installed.

asnp Citrix*

$Today = Get-Date
if(($Today.DayOfWeek) -eq 'Monday')
{$when = $Today.AddDays(-3)}
else{$when = $Today.AddDays(-1)}

$creds          = import-csv "c:\scripts\logs\secureclient.csv"
$CLIENT_ID      = $creds.ID
$CLIENT_SECRET  = $creds.Secret
$CUSTOMER_ID    = $creds.CustomerID
$tokenUrl       = 'https://api-us.cloud.com/cctrustoauth2/root/tokens/clients'

$response       = Invoke-WebRequest $tokenUrl -Method POST -Body @{
  grant_type    = "client_credentials"
  client_id     = $CLIENT_ID
  client_secret = $CLIENT_SECRET
}

$token = $response.Content | ConvertFrom-Json

$headers              = @{
  Accept              = "application/json"
  Authorization       = "CwsAuth Bearer=$($token.access_token)"
  'Citrix-CustomerId' = $CUSTOMER_ID
 }

# Get Licensing Info
$response            = Invoke-WebRequest "https://api-us.cloud.com/licensing/license/enterprise/cloud/cvad/ud/current" -Method Get -Headers $headers
$content             = $response.Content | ConvertFrom-Json
$response.Content | ConvertFrom-Json | ConvertTo-Json -Depth 10
$licensingTotalCount = $content.totalAvailableLicenseCount
$licensingUsageCount = $content.totalUsageCount
$licensingRemaining  = $content.remainingLicenseCount

$connections = Get-BrokerConnectionLog -BearerToken $headers.Authorization  -Filter {BrokeringTime -gt $when} -MaxRecordCount 100000 | Select-Object BrokeringUserName

$CitrixVDIConnected     = (Get-BrokerSession -BearerToken $headers.Authorization  -MaxRecordCount 100000 | Where-Object SessionSupport -eq "SingleSession" | Where-Object SessionState -eq "Active").count
$CitrixVDIDisconnected  = (Get-BrokerSession -BearerToken $headers.Authorization  -MaxRecordCount 100000 | Where-Object SessionSupport -eq "SingleSession" | Where-Object SessionState -eq "Disconnected").count

$ctxUsers = [PSCustomObject] @{

  UniqueCitrixUsers      = ($connections.BrokeringUserName | Select-Object -Unique).count
  CurrentSessions        = (Get-BrokerSession -BearerToken $headers.Authorization -MaxRecordCount 100000 | Select-Object BrokeringUserName).count
  CitrixVDISessions      = $CitrixVDIConnected + $CitrixVDIDisconnected
  CitrixLicensesUsed     = $licensingUsageCount
  CitrixTotalLicenses    = $licensingTotalCount
  CtxLicenseFreePercent  = ((($licensingUsageCount) / $licensingTotalCount ) * 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>"

$body = $ctxUsers | ConvertTo-Html -Head $style 

$date             = Get-Date -Format "MM-dd-yyyy"
$emailFrom        = "someemail@company.com"
$emailto          = "someemail@company.com"
$emailtwo         = "someemail@company.com"
$emailCC          = "someemail@company.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
$email.body       = $body
$smtpserver       = "smtp.company.com" 
$smtp             = new-object Net.Mail.SmtpClient($smtpServer)
$smtp.Send($email)

Links to other articles in the series:

Part 1 Of Cloud Migration Series: Part 1

Part 2 Of Cloud Migration Series: Part 2

Part 4 Of Cloud Migration Series: Part 4

Moving Control Plane To Cloud: Changing Custom PublishedName Property

Next in the series of Cloud Migration…. When migrating the control plane to Citrix Cloud, I found that if you had changed the PublishedName to something other than the default Delivery Group name, they up and vanished and reverted to the Delivery Group name. I didn’t have that many to have to change, but if you had a LOT of them set, it would have not been fun. But…. Powershell again to the rescue!

If you do use custom set PublishedName, then you will need to get a list exported from your old DDC to import. This builds on the other post of setting up your new Powershell SDK.

The script below has parts to run on the old DDC and on a machine with the new SDK installed.

# Script to get custom PublishedName and change from on-prem to Citrix Cloud after ACT import.

# To be ran on old on-prem DDC to get custom PublishedNames.
Get-BrokerDesktopGroup | Select-Object Name, PublishedName | Export-csv C:\scripts\logs\pubslishedname.csv -append -NoTypeInformation

# To be ran on machine with Citrix Cloud SDK installed.
asnp Citrix*

Get-XDCredentials -ProfileName "default"

$pubName = import-csv "C:\scripts\logs\published.csv"

foreach($pub in $pubName){
  
  Get-BrokerDesktopGroup -Name $pub.Name | Set-BrokerDesktopGroup -PublishedName $pub.PublishedName

 }
 
 Get-BrokerDesktopGroup -MaxRecordCount 10000 | Select-Object Name, PublishedName

This is the output you see where the Name and PublishedName are matching the Delivery Group name.

After exporting your CSV from the old DDC and copying the file to your machine with the new SDK installed, you can edit the csv (example here in Notepad++) and make and changes you need to make to it.

After running the second part of the script, you see the changes reflected on the PublishedName field.

Links to other articles in the series:

Part 1 Of Cloud Migration Series: Part 1

Part 3 Of Cloud Migration Series: Part 3

Part 4 Of Cloud Migration Series: Part 4

Moving Control Plane To Cloud: Setting Up Cloud SDK And Authentication Profiles

This will be first in a series of posts relating to what I found in moving to Citrix Cloud and some of the gotchas I encountered.

Update** Added change to switch profiles to: Get-XDAuthentication -ProfileName “Cloud-Test” -Verbose**

This was a good article to get started with using the Cloud SDK to replace your other SDK you installed with Citrix Studio (https://www.citrix.com/blogs/2022/02/03/getting-started-with-powershell-automation-for-citrix-cloud/). Important note, if you install this with Studio installed, Studio will no worky after the installation.

One of the other things you will have to do, is create an API access account that will download a secureclient.csv that you will use to authenticate and allow you to run commands against Citrix Cloud.

Logon to the Citrix Cloud at cloud.com and get authenticated.

Click on the hamburger menu in the upper left.

Click on “Identity and Access Management.”

Click on “API Access.”

Fill out the name and click “Create Client.”

Copy and save the “ID” and “Secret.”

Click to download and save the “secureclient.csv” file to store in a safe location.

I added the “CustomerId” field to my secureclient file to pass to the XDCredential setup. To get this, you can see your CCID on the upper-right hand side underneath your name while you are logged into Citrix Cloud.

Below is what I used to configure my access to do the connections. One thing I noticed, if you name the profile anything other than “default,” it prompts for authentication and caused issues for automated scheduled tasks.

# Script to setup Citrix Cloud credential profile
asnp Citrix*

$secureClientProd = “C:\scripts\logs\secureclient-1.csv"
$secureClientTest = “C:\scripts\logs\secureclient-2.csv"
$xdCredsProd = import-csv $secureClientProd
$xdCredsTest = import-csv $secureClientTest

# Set prod profile
Set-XDCredentials -CustomerId $xdCredsProd.CustomerId -SecureClientFile $secureClientProd -ProfileType CloudAPI –StoreAs "default"

# Set test profile if you have a test cloud account
Set-XDCredentials -CustomerId $xdCredsTest.CustomerId -SecureClientFile $secureClientTest -ProfileType CloudAPI –StoreAs "Cloud-Test"

# List profiles
Get-XDCredentials -ListProfile

# Load credentials
Get-XDCredentials -ProfileName "default"

# To change profile credentials
Get-XDAuthentication -ProfileName "Cloud-Test"

# Clear Cloud credentials if you wish to delete a profile
Clear-XDCredentials -ProfileName "profilename"

So what I did to modify most of the scripts I was using before, was to remove the -AdminAddress and add these lines to the top of the scripts, then proceed business as normal. This allowed me to do the same things I was doing before and pass the API securecred file information.

asnp Citrix*
Get-XDCredentials -ProfileName "default"
From Update section to show the change of the profile

Links to other articles in the series:

Part 2 Of Cloud Migration Series: Part 2

Part 3 Of Cloud Migration Series: Part 3

Part 4 Of Cloud Migration Series: Part 4

Getting And Comparing AgentVersions on VDAs Against Target Version

I was looking at a way to compare versions of VDAs installed on various systems to see what systems needed to be updated. I ran into some issues trying to compare the versions as there are different formats and there was not a consistent numbering system going back to 7.15 that I could discern. So with some assistance from https://www.linkedin.com/in/douglas-ruehrwein-56835869/, I was able to get the version check working correctly. This ended up comparing to the target version and returning anything that was less than the target version. I didn’t want to target anything newer than the target as I had reasons for those particular systems to be running a newer VDA. You can combine this with the VDA upgrade script to output the DNSNames of the machines to upgrades machines outside of the target version.

This was first attempt and realized some machines didn’t show HostedMachineName.
This was the second attempt and got it to show the DNSName as well and this helped identify the Linux VDA machines.
Final using [System.Version] to compare the versioning numbers. This was the expected output.
# Script to get VDA versions below target version. This was done in PowerShell ISE 5.1 against 1912LTSRCU5 DDCs.
$adminAddress = "deliverycontroller.fqdn"
$date = Get-Date -Format MMddyyyy
$outputName = "VDAToUpgrade"
$report = @()
[System.Version]$targetVersion = "1912.0.5000.5174"
$getMachines = Get-BrokerMachine -AdminAddress $adminAddress -MaxRecordCount 1000000

foreach($machine in $getMachines){
  $line                   = "" | Select HostedMachineName, DNSName, AgentVersion, WillBeUpgraded
  $testVersion            = $machine.AgentVersion
  
  $line.HostedMachineName = $machine.HostedMachineName
  $line.DNSName           = $machine.DNSName
  $line.AgentVersion      = $machine.AgentVersion
  
  if([System.Version]$testVersion -ge [System.Version]($targetVersion)){

    $line.WillBeUpgraded  = "Current Version Or Newer"
  
  }
  
  if([System.Version]$testVersion -lt [System.Version]($targetVersion)){

    $line.WillBeUpgraded  = "Yes"
  
  }
    
  $report += $line
  
}

$report | Export-Csv -Path c:\scripts\logs\$date-$outputName.csv -Append -NoTypeInformation

# To see only the versions that are not matching the target version
$report | Where-Object WillBeUpgraded -eq "Yes"

Are You The Keymaster!? : Script To Change ListOfDDCs in Registry

You have an upcoming change and some new DDCs you brought online. You may be changing out to Citrix Cloud (you better be), and you may need to change the ListofDDCs to you Cloud Connector. Sometimes GPO may take a minute to reflect what you want set. You can use this to change the ListOfDDCs quickly. You can also add the ListofSSIDs if that is something that you use by adding another registry name and value in your script block. I have the Get-ItemProperty used twice to get the result of what was set before the change and to show the reflected change. I just like to doubly confirm something and make sure something hinky was not afoot.

# Script to change DDCs on a group of Citrix servers. You will need access to the remote servers and firewall access with PowerShell.
$listServers = Get-Content c:\scripts\logs\svrlist.txt
$date        = Get-Date -Format MMddyyyy
$report      = @()

foreach($srv in $listServers) {

  $scriptBlock = {
    
    $regName  = "ListOfDDCs"
    $regValue = "DDC1 DDC2 or CC1 CC2"
    Get-ItemProperty -Path HKLM:\Software\Citrix\VirtualDesktopAgent
    Set-ItemProperty -Path HKLM:\Software\Citrix\VirtualDesktopAgent -Name $regName -Value $regValue
    Get-ItemProperty -Path HKLM:\Software\Citrix\VirtualDesktopAgent
       
  }

  $ddcUpdate  = Invoke-Command -ComputerName $srv -ScriptBlock $scriptBlock
  
  $report += $ddcUpdate
  
}

$report | Out-File c:\scripts\logs\$date-ddcchange-list.txt

You Wanted VDA Install With Applications!? New And Improved With Application Install On Base Domain Joined Server!

Powered By Philosoraptor Inc.

Building off of the VDA Upgrade script, this adds the additional components from a base server install. You will need to have the software packages in a folder, c:\software\ for this example. This script checks for Remote Desktop Services being installed and if not, installs Remote Desktop Services prior to kicking off the clean install of the Citrix VDA. I found this method worked best when trying to do a clean install. You can adjust the delay on the Add Minutes if you need more time before kicking off the base install. With SSD and decent procs, it shouldn’t take too long to install RDS and the Citrix VDA.

This batch file contains installs for: Acrobat DC; MS Edge; Google Chrome; Office 2016×86. The Install-Edge.ps1 is included below. You will need to create an MSP and config.xml for your Office configuration. Mode.reg is included as it sets the license mode to “Per User” for RDS Licensing mode. If you have any custom registry edits, you can included similar to mode.reg to import those registry settings as part of the install. You can modify the baseinstall.bat to add any programs you wish to add. Just make sure you can do the setup of the app in an unattended mode so that you can run it. When the install is complete, just remember to cleanup the installers in the c:\software folder to save space.

$vdilist = get-content c:\scripts\logs\servers.txt
$source = "\\placewherefilesare"
$dest = "c$\software\vdaupgrade"

  foreach($vdi in $vdilist){
    Write-Host "Working on $vdi"
    if (!(Test-Path -Path \\$vdi\c$\software\vdaupgrade)) {
        New-Item -ItemType Directory -Path \\$vdi\c$\software -Name vdaupgrade
        Copy-Item "\\$source\install.bat" -Destination \\$vdi\$dest -Force
        Copy-Item "\\$source\baseinstall.bat" -Destination \\$vdi\$dest -Force
        Copy-Item "\\$source\VDAServerSetup_1912.exe" -Destination \\$vdi\$dest -Force
    }
    else {
        Copy-Item "\\$source\install.bat" -Destination \\$vdi\$dest -Force
        Copy-Item "\\$source\baseinstall.bat" -Destination \\$vdi\$dest -Force
        Copy-Item "\\$source\VDAServerSetup_1912.exe" -Destination \\$vdi\$dest -Force
    }
  
    $rdsCheck = (invoke-command -ComputerName $vdilist -ScriptBlock {get-windowsfeature | where name -like "rds-rd-server" | select InstallState })

    if($rdsCheck.InstallState.value -eq "Available") {
        
        Copy-Item "\\$source\baseinstall.bat" -Destination \\$vdi\$dest -Force
        
        Invoke-Command -ComputerName $vdi -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
          $taskName = "VDAInstall"
          $taskDescription = "Citrix VDA Install"

        Register-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -TaskName $taskName -Description $taskDescription
        }
        
        Invoke-Command -ComputerName $vdi -Scriptblock {
          $time = (Get-Date).AddMinutes(7)
          $action = New-ScheduledTaskAction -Execute 'c:\software\vdaupgrade\baseinstall.bat'
          $trigger = New-ScheduledTaskTrigger -Once -At $time
          $principal = New-ScheduledTaskPrincipal  -RunLevel Highest -UserID "NT AUTHORITY\SYSTEM" -LogonType S4U
          $taskName = "BaseInstall"
          $taskDescription = "Base Software Install"

        Register-ScheduledTask -Action $action -Trigger $trigger -Principal $principal -TaskName $taskName -Description $taskDescription 
        }

        Invoke-Command -ComputerName $vdi -ScriptBlock {
          Add-WindowsFeature rds-rd-server
          Restart-computer
        }
    }
   

  }
  

baseinstall.bat
 
@ECHO ON
change user /install
REM pause
timeout 5

net localgroup "Remote Desktop Users" /add "domain1\domain users" "domain2\domain users"
REM pause
timeout 5

REG IMPORT C:\software\mode.reg
REM pause
timeout 5

C:\software\AcrobatRdrDC\setup.exe /sAll /ini Setup.ini
REM pause
timeout 10

cd C:\software\MS-Edge
powershell -File ".\Install-Edge.ps1" -MSIName "MicrosoftEdgeEnterpriseX64.msi" -ChannelID "{56eb18f8-b008-4cbd-b6d2-8c97fe7e9062}" -DoAutoUpdate "True"
REM pause
timeout 5

msiexec.exe /i "C:\software\Google-Chrome\64B\GoogleChromeStandaloneEnterprise64.msi" /qn
REM pause
timeout 5

C:\software\Office\setup.exe /config .\ProPlus.WW\config.xml /adminfile CITRIX.MSP
REM pause
timeout 10

change user /execute
REM pause
timeout 5

C:\Windows\system32\schtasks.exe /delete /tn BaseInstall /f
C:\Windows\System32\timeout.exe /t 5
C:\Windows\System32\shutdown.exe /r /t 20 /f
del c:\software\vdaupgrade\baseinstall.bat /F
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
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

Install-Edge.ps1
param
(
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [ValidatePattern('^[a-zA-Z0-9]+.[m|M][s|S][i|I]$')]
    [string]$MSIName,
        
    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [ValidatePattern('^{[0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12}}$')]
    [string]$ChannelID,

    [parameter(Mandatory=$true)]
    [ValidateNotNullOrEmpty()]
    [string]$DoAutoUpdate
)

# See if autoupdate is false
if($DoAutoUpdate -eq $false)
{   
    # Registry value name is in the format "Update<{ChannelID}> where ChannelID is the GUID
    Set-Variable -Name "AutoUpdateValueName" -Value "Update$ChannelID" -Option Constant
    Set-Variable -Name "RegistryPath" -Value "HKLM:\SOFTWARE\Policies\Microsoft\EdgeUpdate" -Option Constant

    # Test if the registry key exists. If it doesn't, create it
    $EdgeUpdateRegKeyExists = Test-Path -Path $RegistryPath

    if (!$EdgeUpdateRegKeyExists)
    {
        New-Item -Path $RegistryPath
    }

    # See if the autoupdate value exists
    if (!(Get-ItemProperty -Path $RegistryPath -Name $AutoUpdateValueName -ErrorAction SilentlyContinue))
    {
        New-ItemProperty -Path $RegistryPath -Name $AutoUpdateValueName -Value 0 -PropertyType DWord
    }

   $AutoupdateValue = (Get-ItemProperty -Path $RegistryPath -Name $AutoUpdateValueName).$AutoUpdateValueName

   # If the value is not set to 0, auto update is not turned off, this is a failure
    if ($AutoupdateValue -ne 0)
    {
        Write-Host "Autoupdate value set incorrectly"
        return -1
    }
}
# Install the Edge MSI
return (Start-Process msiexec.exe -Wait -PassThru -ArgumentList "/i $MSIName /q").ExitCode


mode.reg
Windows Registry Editor Version 5.00

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server\RCM\Licensing Core]
"LicensingMode"=dword:00000004

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
    
    

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

Page 3 of 7

Powered by WordPress & Theme by Anders Norén