XenApplePie

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

You Will Unblock Those Files! : Script To Unblock Powershell Files

So you’ve downloaded a cool new module. Your buddy gave you a module to test out. You copy it over and start looking to load it. You get the oh no! message. Your computer thinks you are being shady and blocks the file.

You have heard tale of an option to right-click EVERY single file and unblock the files.

All you want to do is unblock the files and load the module (at your own risk of course!). Well, then. Do it! Copy the module to your module directory in your path. Set the path to where it is. Iterate through those files, then load that module!

# Script to unblock files

$path = 'C:\Program Files\WindowsPowerShell\Modules\somemodule'
$files = Get-ChildItem -Path $path -Recurse | ForEach{$_.FullName}
$module = $files | Select-String -Pattern "psm1"

ForEach($file in $files){

  Unblock-File $file -Confirm:$false

}

Import-Module $module -Force

Changing HypervisorConnectionUid for VDI machines

So you have a new / different vCenter you want to move your VDI machines to. For the power management part, you will need to have the other hypervisor connection configured. You can run the script below to change the hypervisor connection on your VDI machines. You will want to make sure you have the VMs powered down before beginning. The first steps are just information gathering. This is part of migrating machines to new hardware. I will be adding the other pieces at a later date.

First you will need to get your hypervisor names and Uids with this command: Get-BrokerHypervisorConnection | Select-Object Name, HypHypervisorType, Uid.

Then you can get a list of machines with the hypervisor connection you want to change from. This was just getting the first machine that had the hypervisor I wanted to change from. Machine was already moved to a new Uid but it would be 2 in this case. Get-BrokerMachine -MaxRecordCount 100000 | Where-Object SessionSupport -eq “SingleSession” | Where-Object HypervisorConnectionUid -eq “3” | Select-Object -first 1 | Select-Object HostedMachineName, HypervisorConnectionName, HypervisorConnectionUid

<#  Script to change Hypervisor connection and power systems back up. This was done with PowerShell ISE 5.1, default profile configured on CitrixCloud SDK, and ESXi 7.0 with connection
    to vCenter. This was built from Ben McGirt and slightly modified to get the machine names with a specific configured HypervisorConnection via Get-BrokerMachine command.
    As it is best to change these settings and power the VM on, you will need to have the VMs powered down before beginning. The Uid in the example is "2" to get the machines using a different
    HypvisorConnectionUid that you wish to change from and setting in this example to HypervisorConnectionUid "3."
#>

Get-XDAuthentication -ProfileName "default"

$getVDIMachines = Get-BrokerMachine -MaxRecordCount 100000 | Where-Object SessionSupport -eq "SingleSession" | Where-Object HypervisorConnectionUid -eq "2" | Select-Object HostedMachineName, HypervisorConnectionName, HypervisorConnectionUid

$citrixVMs = $getVDIMachines.HostedMachineName
 
Function PowerOnVM ([string] $Name) #, [string] $Hostname)
#From https://thecloudxpert.net/2016/04/25/howto-power-on-a-vmware-virtual-machine-with-powercli-powercli-101/
{
    $VM = Get-VM -Name $Name
    Switch ($VM.PowerState)
    {
        PoweredOn { Write-Host "$VM already Powered On.";break}
        PoweredOff { Write-Host "Powering on $VM"; Start-VM -VM $VM;break}
        Suspended { Write-Host "$VM suspended.";break}
        Default {break}
    }
}
 

ForEach ($VM in $CitrixVMs){

    Set-BrokerMachine -MachineName ("*\" + $VM) -HypervisorConnectionUid 3
    Start-Sleep -Seconds 2
    PowerOnVM $VM
    
    }
 

What Do You Want? Information! : Getting Application Information And Exporting To CSV

You know you have a LOT of apps. But you want to know about 1 app in particular. You could go to Studio and look. You could peruse the various pieces and parts to and get what you want. Or…. you could just grab it via script. So that is what we gonna do here. We are going to present a grid view of the applications you have published, select one of them, and give you all the information you have room for! And, for an unlimited time offer, export to csv! This requires having setup your Citrix Cloud authentication and using your secureclient.csv to access.

Selection menu
Output from script
CSV output
# Get Citrix Application Info
asnp Citrix*
Get-XDAuthentication -ProfileName "default"

$application             = Get-BrokerApplication -MaxRecordCount 100000 | Select-Object ApplicationName, Enabled, AssociatedApplicationGroupUids, AllAssociatedDesktopGroupUids | Out-GridView -Title "Applications" -PassThru
$date                    = Get-Date -Format MMddyyyy
$a                       = 1
$d                       = 1

$app                     = $application.ApplicationName
$appGroupUids            = @($application.AssociatedApplicationGroupUids)
$deliveryGroupUids       = @($application.AllAssociatedDesktopGroupUids)
$appGroupUidsCounts      = ($appGroupUids).Count
$deliveryGroupUidsCounts = ($deliveryGroupUids).count
$ctxAppInfo              = [PSCustomObject]@{}
$ctxAppInfo | Add-Member -NotePropertyName "ApplicationName" -NotePropertyValue ($application).ApplicationName -Force
$ctxAppInfo | Add-Member -NotePropertyName "Enabled" -NotePropertyValue ($application).Enabled -Force

if($appGroupUidsCounts -gt 0){

  foreach($appGroups in $appGroupUids){
    
    $applicationGroupInfo    = (Get-BrokerApplicationGroup -Uid $appGroups)
    $applicationGroupNames   = ($applicationGroupInfo).ApplicationGroupName
    $ctxAppInfo | Add-Member -NotePropertyName "ApplicationGroups-$a" -NotePropertyValue $applicationGroupNames -Force

    if($applicationGroupInfo.AssociatedUserNames -ne $null){
      
      $applicationGroupUsers = ($applicationGroupInfo).AssociatedUserNames -join ';'
      $ctxAppInfo | Add-Member -NotePropertyName "ApplicationGroupUsers-$a" -NotePropertyValue $applicationGroupUsers -Force
      
    }
    
    $a++
  } 
}

if($deliveryGroupUidsCounts -gt 0){
  
  foreach($deliveryGroup in $deliveryGroupUids){
    
    $deliveryGroupInfo  = Get-BrokerDesktopGroup -Uid $deliveryGroup
    $deliveryGroupNames = $deliveryGroupInfo.PublishedName
    $deliveryGroupUsers = (Get-BrokerAccessPolicyRule -DesktopGroupName "$deliveryGroupNames")
    $dgUserCheck        = ($deliveryGroupUsers).AllowedUsers
    $ctxAppInfo | Add-Member -NotePropertyName "DeliveryGroupNames-$d" -NotePropertyValue $deliveryGroupNames -Force
    
    if($dgUserCheck -eq "Filtered"){
    
      $deliveryGroupUsers  = ($deliveryGroupUsers).IncludedUsers.Name -join ';'
      $ctxAppInfo | Add-Member -NotePropertyName "DeliveryGroupUsers-$d" -NotePropertyValue $deliveryGroupUsers -Force
    }
    if($dgUserCheck -eq "AnyAuthenticated"){
      $ctxAppInfo | Add-Member -NotePropertyName "DeliveryGroupUsers-$d" -NotePropertyValue "AnyAuthenticated" -Force
    }
    
    $deliveryGroupMachines        = Get-BrokerMachine -MaxRecordCount 100000 | Where-Object DesktopGroupName -eq "$deliveryGroupNames"
    $deliveryGroupMachineNames    = ($deliveryGroupMachines | Select-Object MachineName).MachineName -join ';'
    $ctxAppInfo | Add-Member -NotePropertyName "DeliveryGroupMachines-$d" -NotePropertyValue $deliveryGroupMachineNames -Force
    
    
    $d++
  }

 
}

$ctxAppInfo | Export-Csv C:\scripts\logs\$date-$app-App-Info.csv -Append -NoTypeInformation

A Wild Citrix 2203 LTSR CU1 Appears!

https://www.citrix.com/downloads/citrix-virtual-apps-and-desktops/product-software/citrix-virtual-apps-and-desktops-2203ltsr-cu1-all-editions.html

Go and check it out!

Moving Control Plane To Cloud: Migrating Citrix Director To Cloud

Fourth in the series of moving the control plane to Citrix Cloud…. So you have moved your control plane and now you need some monitoring via the wonders of Director. You can do this… There are some things that you have to consider. First, historical data DOES NOT migrate as part of using the Automated Configuration Tool (ACT). I looked over the list of things that it did, and that was not one of the things listed as migrating. I did learn through a ticket that moving the historical data is not at this time supported, nor was there a tool to move said data. For those customers that heavily rely on the reporting data available, you will need to plan accordingly. This presents a difficulty that an on-prem version of Director would need to be maintained for a year (or whatever grooming schedule you have defined) to allow the data to be accessed while the new data was populating. This can cause issues with people needing to access BOTH locations in order to get accurate data. Also, default for Premium / Platinum customers is 90 days of historical data in Director. Advanced licensing provides 31 days. If you need more time than that, you will need to open a ticket with Citrix to get that time extended which can take 1 – 2 weeks. (More information on Citrix Director in Cloud can be found here: Director On Citrix Cloud )

Now if you have setup AD Connect to connect to Citrix Cloud with company credentials, you can publish Director with the accounts people already use to login with at their company. After moving, there is also a post that will come about how scopes are affected and you have to reassign your access groups used in your company to the new Monitor role that is created for EACH scope they need access.

To confirm that you have AD Connect configured, you can check the Identity And Access Management on the hamburger menu on your Citrix Cloud login.

It should show Azure Active Directory and next to https://citrix.cloud.com/go should be your company name selected when you connected AAD.

If this is showing as connected, you can publish Director to your helpdesk group.

These are the arguments for publishing Director with Microsoft Edge:

ApplicationName : Director
CommandLineArguments : https://xenapp.cloud.com/monitor
CommandLineExecutable : %ProgramFiles(x86)%\Microsoft\Edge\Application\msedge.exe
WorkingDirector : %ProgramFiles(x86)%\Microsoft\Edge\Application

Once you have published the app to your users, the directions below will allow them to connect with their AAD credentials.

Once the user clicks on the link, they will be presented with the Citrix Cloud login.

The user will need to click on “Sign in with my company credentials.”

The user will need to enter the company name assigned at the configuration of AAD Connect.

If you are using MFA with Office 365 or another provider, you will have needed to setup your MFA app / other methods of verification.

After completing login, user will be presented Director and be able to continue duties as assigned by roles.

Links to other articles in the series:

Part 1 of migration series Setting Up Cloud SDK And Authentication Profiles: Part 1

Part 2 of migration series Changing Custom PublishedName Property: Part 2

Part 3 of migration series Migrating Citrix Daily User Report: Part 3

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

Page 3 of 11

Powered by WordPress & Theme by Anders Norén