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

Tag: script

Copy Training Video And Publish As App

Sometimes you need users to see training videos. You may have to copy it to several servers. You may need to copy several videos to several servers. There might be an instance where it is a new implementation or you have had videos there before. This will copy videos to a group of servers in a Delivery Group and publish the app to the Application Group of your choosing.

$remoteMachines        = (Get-BrokerMachine -MaxRecordCount 100000 | Where-Object DesktopGroupName -Match "DeliveryGroupName" | Select-Object DNSName).DNSName
$sourcePath            = "PathToVideo.fqdn"
$videosToCopy          = Get-ChildItem -Path $sourcePath | Select-Object Name
$destinationPath       = "c$\training-video-folder"
$applicationType       = "HostedOnDesktop"
$commandLineArguments  = "c:\training-video-folder"
$commandLineExecutable = "%ProgramFiles(x86)%\Windows Media Player\wmplayer.exe"
$workingDirectory      = "%ProgramFiles(x86)%\Windows Media Player"
$applicationGroup      = "App Group Name"
$iconUid               = "21"

$totalItems            = $remoteMachines.Count
$currentItem           = 0
$percentComplete       = 0

foreach($remote in $remoteMachines){
  Write-Host "Checking if folder " $destinationPath " exists on " $remote
  if (!(Test-Path -Path "\\$remote\$destinationPath")) {
        Write-Host "Creating folder " ($destinationPath).Split('\')[1] " on $remote"      
        New-Item -ItemType Directory -Path "\\$remote\c$" -Name ($destinationPath).Split('\')[1]

  foreach($video in $videosToCopy){
    Write-Progress -Activity "Copying $video to $remote" -Status "$percentComplete% Complete:" -PercentComplete $percentComplete
    Copy-Item "\\$sourcePath\$video" -Destination "\\$remote\$destinationPath\" -Force
    $percentComplete = [int](($currentItem / $totalItems) * 100)

foreach($vid in $videosToCopy){

$videoName = ($vid.Name)
$shortName = ($vid.Name).Split('.')[0]

New-BrokerApplication -ApplicationType "$applicationType" -Name "$shortName" -BrowserName "$shortName" -CommandLineExecutable "$commandLineExecutable" -CommandLineArguments "$commandLineArguments\$videoName" -Description "$shortName" -WorkingDirectory "$workingDirectory" -ApplicationGroup "$applicationGroup" -IconUid "$iconUid"


Quick Function To Find User VDI

Sometimes you need to find a user’s VDI machine to work on it. This function will do that for you. I typically use the last name as the search to limit the scope of the machines found. It will find all machines that contain any part of the string you enter. It also shows the MachineName which includes the domain\machinename to help locate the user machine. You can add other parameters such as AgentVersion if desired. I limited the scope to not include floating pool (Random) assigned machines. For a list of all fields of Get-BrokerMachine that can be selected in the function with Select-Object, please see this link: Get-BrokerMachine Options

# Requires being connected to Citrix Cloud with DaaS SDK. 
Function Get-VDI {
        [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
    Process {
       $userToFind = '*' + $VDIUserName + '*'
       $getVDIs = Get-BrokerMachine -MaxRecordCount 100000 | Where-Object SessionSupport -eq "SingleSession" | Where-Object AllocationType -eq "Static" | Where-Object AssociatedUserNames -like "$userToFind" | Select-Object AssociatedUserNames, MachineName, RegistrationState, InMaintenanceMode, SessionCount
    End {
Example with user that has 4 desktops assigned.

Also to make it is easy for the day to day, you can add this to your profile with notepad $profile and copy and paste it there and reload.

Get User Info From Email Address

If you sometimes have to find a user list and get group memberships, it can be a chore. It doesn’t have to be. You too can experience a scripted approach to getting that data. This will search the AD Forest’s sub domains that you have rights to at least read to get user memberships that follow a specific pattern. Very useful when you have one domain for groups and users from many domains. Universal groups are great for that. You just need to populate the referenced “useremail.txt” file with user email addresses and deleting any white space in the file. You can also get additional information in the report by adding in the “line = “” | Select-Object” and adding additional fields and then matching the name designated with the “line.Fields = syntax.”

# Script to search domains in forest for users via user email address. You could also search via UPN by replacing EmailAddress with UserPrincipalName.
# This requires AD module, read rights to forest / domains, and user group pattern. Ran with ISE and tested on desktop / server platforms. This also uses
# the global catalog lookup as seen referenced by Get-ADUser -server domain:3268. Assistance from in making this.
$debug      = $true
$domains    = (Get-ADForest).Domains
$date       = Get-Date -Format MMddyy
$userExists = ""
$reportName = "nameofreport.csv"
$report     = @()

$emailList  = Get-Content -Path "c:\scripts\logs\useremail.txt"

$emailSam   =  foreach($user in $emailList) {
    (Get-ADUser -server domain:3268 -f {EmailAddress -eq $user}).SamAccountName

$users      = $emailSam

foreach ($domain in $domains){
    if ($debug){Write-Host $domain -ForegroundColor Cyan}
    foreach ($user in $users) {
            $userExists = Get-ADUser $user -Server $domain -ErrorAction Stop
        Catch {
            if ($debug){Write-Host "$user not found in $domain" -BackgroundColor yellow -ForegroundColor black}
            $userExists = $null
        if ($userExists -ne $null){
            if ($debug){ Write-Host "Found $user in $domain domain" -BackgroundColor Cyan -ForegroundColor Black}
            $group = $userExists | Get-ADPrincipalGroupMembership | Where-Object Name -like "GroupNamePattern*" -ErrorAction Stop
            $line          = "" | Select-Object Name, Domain, RealName, Group
            $line.Name     = $user
            $line.Domain   = $domain
            $line.RealName = $
            $line.Group    = $ -join "; "
            if ($debug){ $line }
            $report       += $line
            $users         = $users -ne $user

$report | Export-Csv "c:\scripts\logs\$date-$reportName" -Append -NoTypeInformation

Get That Outta Here! : Releasing Citrix Cloud User Licenses Via API In Accordance With Citrix EULA

*Update. Changes made due to API only allowing 100 licenses to be released at one time.*

Building on getting the licenses on the last post, how about a way to release them? Obviously you will need to follow the guidelines laid out by Citrix EULA on when you can release licenses: Citrix License Usage.

You do have the option to use the console in Citrix Cloud, but if you prefer to do things via script, you can do that as well!

Important note to make…. The format of the consumerList is what presented the biggest issue. With a bit of assistance from a teammate, it was possible to get the proper way to format the data.

This is the format to have the data in:

Link to developer API document

You’ll notice the consumerList = @() in there. It is expecting to have an array of consumerList be passed to it. If you do not format this way, you will get invalid character errors.

Example of format that will fail all so miserably and give you that wonderful invalid character error:

This is an example of the output that will work and prevent frustration:

Example with five of users:

Example of result of successful release of 5 users:

# Script to release licenses from Citrix Cloud. Tested with PowerShell ISE. Also requires secure-client.csv with ID, Secret, and CustomerID in CSV to pass to Citrix Cloud.
# This must be used in accordance with Citrix EULA for users not having accessed in 30+ day period. Example below is targeting where users that have not logged in over 60 days.
# This will report on what users are in that group over 60 days. You can do a quick check using $releaseUsers.count before doing the release to see how many licenses will be released.
# ALWAYS check the exported report of users before releasing to make sure you are releasing the licenses you are expecting to.

$date           = (Get-Date).AddDays(-60)
$creds          = Import-Csv "c:\scripts\secure-client.csv"
$CLIENT_ID      = $creds.ID
$CLIENT_SECRET  = $creds.Secret
$CUSTOMER_ID    = $creds.CustomerID
$tokenUrl       = ''
$reportName     = "user-license-toRelease.csv"
$getDate        = Get-Date -Format MMddyyyy
$reportLocation = "C:\scripts\logs"

$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
$resourceLocUrl = "$CUSTOMER_ID/sites"
$response       = Invoke-WebRequest $resourceLocUrl -Headers $headers
$content        = $response.Content | ConvertFrom-Json
$siteID         = $

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

$consumerList = Invoke-RestMethod "" -Method GET -Headers $headers | Select-Object consumerList -ExpandProperty consumerList -ExcludeProperty consumerList
$releaseUsers = $consumerList | Where-Object {[DateTime]$_.latestLogonTime -lt $date} 
$releaseUsers | Export-Csv "$reportLocation\$getDate-$reportName" -Append -NoTypeInformation

if($releaseUsers.count -gt 100){

$smallerCounter = $releaseUsers.Count
$toSkip = 0


$smallerList = $releaseUsers | Select-Object -Skip $toSkip -first 100

$body = @{
    productEdition = "XAXDFull"
    licenseType    = "user"
    consumerList   = @(
} | ConvertTo-Json

Invoke-RestMethod "" -Method POST -Body $body -Headers $headers -ContentType 'application/json'

$toSkip += 100
$smallerCounter -= 100

} Until ($smallerCounter -lt 100)

$smallerList = $releaseUsers | Select-Object -Skip $toSkip -first $smallerCounter

$body = @{
    productEdition = "XAXDFull"
    licenseType    = "user"
    consumerList   = @(
} | ConvertTo-Json

Invoke-RestMethod "" -Method POST -Body $body -Headers $headers -ContentType 'application/json'


Powered by WordPress & Theme by Anders Norén