Generate a Citrix Desktop Report Based on Active Directory Users

In this post, we are going to merge data from two different sources to generate a report that can provide insight into your Citrix environment. I will show you how to combine Active Directory data for the users in your domain with Citrix data. This report will provide you with the following knowledge:

  • Users that do not have a Citrix Desktop assigned
  • Users that have multiple Citrix Desktops assigned
  • Users that have never used their assigned Citrix Desktop
  • The last time your users connected to their Citrix Desktop
  • Percentage of Active Directory Users that have a Citrix Desktop assigned (manual math required)

Prerequisites

There are a few things needed in order to generate this report. The most obvious is the need for an Active Directory domain with users. If you are going to utilize a dedicated server or desktop to run the report, then you will need to install the RSAT Active Directory Domain Services Tools. RSAT is now included directly from within Windows 10. In order to get it installed, search for “Manage optional features” from the start menu and then click on “Add a feature”. Check the box for “RSAT: Active Directory Domain Services and Lightweight Directory Services Tools” and click “Install”. Once it’s finished installing, the Active Directory PowerShell commands found in the script will run. The second most obvious is to have at least one Citrix Delivery Controller and Desktops assigned to users. The Citrix PowerShell snap-ins that are needed are installed automatically when you install a Delivery Controller or Studio. Ensure that you are running your scripts from a machine that has Studio installed.


Code

GitHub

Time to take a look at the code involved to achieve this report. First, we need to import the custom module that we created in my previous post. Alternatively, you could hard code the Citrix Delivery Controllers directly into the $Random_DCs variable. Then we need to import the Citrix PowerShell Snapins.

### Custom module we made in previous blog post
Import-Module "\\path\to\Infra_Module.psm1" -Force

### Citrix Snapin
try{
    ### Adding Citrix powershell snapins
    Add-PsSnapin *Citrix* -ErrorAction Stop
}
Catch [Exception]{ ### If the command inside the try statement fails the error will be outputted
    Write-host "Citrix Powershell snapins are not present, install the snapins and try again. Script is exiting..." -BackgroundColor Red -ForegroundColor White
    Return
}

Next, we will declare the variables used for the script. We need to specify where the csv file will be written to. You can specify a UNC path or a local directory. By using the Infra_One_Random_Each_Site function from the custom module referenced above, we can dynamically retrieve a random delivery controller from multiple Citrix sites. So, if you have multiple sites, we can search across all of them for user assigned desktops. The final line in this code snippet is just to setup the progress bar that will be used later.

$Time = Get-Date -Format "yyyy-MM-dd_HHmm"
$csvfile = "\\path\to\AD_Users_to_Citrix_Report_$Time.csv"
### Grabbing one random Citrix Delivery Controller server from each site based on the given environment, type and active status
$Random_DCs = Infra_One_Random_Each_Site -Environment prod -Type citrix_dc -Active true ### Custom module we made in previous blog post
$i=0 # setting variable to 0 for the start of the progress bar

Below is the function that we utilize to append the results to the csv file after merging Active Directory and Citrix data together.

Function Write_to_csv {
    ### Creating a custom object to hold all of the pertinent data so we can write it to csv
    $outputcsv = [PSCustomObject] @{
    VM = $VM
    VM_IP = $IP
    User_ID = $AD_User.SamAccountName
    User_DisplayName = $AD_User.DisplayName
    User_Email = $AD_User.Mail
    User_Location = $AD_User.City
    User_Department = $AD_User.Department
    User_Title = $AD_User.Title
    User_Created = $AD_User.whenCreated
    Summary_State = $SummaryState
    Last_Connection = $LastConnection
    VM_Created = $Created
    EndPoint_Name = $EndPoint
    EndPoint_IP = $EndPoint_IP
    }
    ### Appending that custom object to the csv file
    $outputcsv | Export-Csv -Path $csvfile -Append -NoTypeInformation
}

Here we are getting all of the enabled Active Directory users that have a mail and manager value. This may not be an adequate way to identify “real” users in your domain. The goal here is to eliminate any service accounts or accounts that are not specifically tied to a real person.

$AD_Users = Get-ADUser -Filter {Enabled -eq $true}  -Properties UserPrincipalName, City, StreetAddress, Department, Title, DisplayName, whenCreated, mail, manager | Select-Object DisplayName, SamAccountName, UserPrincipalName, City, StreetAddress, Department, Title, whenCreated, mail, manager | Where-Object {$_.mail -ne $null -and $_.manager -ne $null}

Finally, we come to the section of code that does the foreach loop on all the users that are returned from the above Get-ADUser command. The first two lines in the foreach loop are just to create a progress bar so you know how far along the script is; this can be especially helpful if you have a large domain. Then it goes through each Citrix DC and gets assigned Citrix Desktops for that given user. This utilizes the User Principal Name because it is a common value between both Citrix and Active Directory. Next, the script will determine if the user has no Desktops assigned, multiple desktops or just one. From there it will take the data from Active Directory and Citrix (if any) and calls the Write_to_csv function to append the data to the csv file. This continues until all users have been parsed through. Once the script is complete you will have a fully populated csv file with all Active Directory users and their assigned Citrix Desktop that can be filtered and sorted in Excel to reveal data important to you.

Foreach ($AD_User in $AD_Users) {
    $i++ ### incrementing the progress bar by 1 each loop through
    Write-Progress -Activity "All AD Users" -Status ("Checking : {0}" -f $AD_User.SamAccountName) -PercentComplete ($i/$AD_Users.count*100) -Id 0 ### Progress bar
    $Desktop = @() ### creating blank array
    ### Foreach statement to go through each returned Citrix DC and find desktops associated with a given user
    Foreach ($Random_DC in $Random_DCs){
        $Desktop += Get-BrokerDesktop -AssociatedUserUPN $AD_User.UserPrincipalName -DeliveryType DesktopsOnly -AdminAddress $Random_DC -MaxRecordCount 999999 | Select-Object Hostedmachinename, LastConnectionTime, SummaryState, ClientName, ClientAddress, IPAddress, AssociatedUserNames, AssociatedUserUPNs | Sort-Object -Property Hostedmachinename
    }
    #####################################################################
    ### If the given AD User DOESN'T have an assigned Citrix Desktop, we will write to the csv that none is assigned
    If (-not $Desktop){
        $VM = "None Assigned"
        $IP = "None Assigned"
        $SummaryState = "None Assigned"
        $LastConnection = "None Assigned"
        $EndPoint = "None Assigned"
        $EndPoint_IP = "None Assigned"
        $Created = "None Assigned"
        #####################################################################
        ### Calling function to write to CSV
        Write_to_csv
        ### Cleaning up variables
        Clear-Variable VM, IP, Summary_State, LastConnection, Created, EndPoint, EndPoint_IP -ErrorAction SilentlyContinue
        #####################################################################
    }
    #####################################################################
    ### If the given AD user DOES have an assigned Citrix Desktop, we will write the details for it to csv
    Else{
        #####################################################################
        ### If the given AD user has more than one Citrix Desktop assigned we will account for all of them
        If (($Desktop.HostedMachineName).count -ge 2){
            Foreach ($item in $Desktop){
                $VM = $item.Hostedmachinename
                $IP = $item.IPAddress
                $SummaryState = $item.SummaryState
                ### Grabbing the date the active directory computer object was created
                $Created = Get-ADComputer $item.Hostedmachinename -Properties Created | ForEach-Object {$_.Created}
                ### If the AD user has never connected to the Citrix Desktop we will add Never Connected to the csv file for those fields
                If ($null -eq $item.LastConnectionTime){
                    $LastConnection = "Never Connected"
                    $EndPoint = "Never Connected"
                    $EndPoint_IP = "Never Connected"
                }
                Else{
                    $LastConnection = $item.LastConnectionTime
                    $EndPoint = $item.ClientName
                    $EndPoint_IP = $item.ClientAddress
                }
                #####################################################################
                ### Calling function to write to CSV
                Write_to_csv
                ### Cleaning up variables
                Clear-Variable VM, IP, SummaryState, LastConnection, Created, EndPoint, EndPoint_IP -ErrorAction SilentlyContinue
                #####################################################################
            }
        }
        #####################################################################
        ### If the given AD user has only one Citrix Desktop assigned we will write it to the csv
        Else {
            $VM = $Desktop.HostedMachineName
            $IP = $Desktop.IPAddress
            $SummaryState = $Desktop.SummaryState
            ### Grabbing the date the active directory computer object was created
            $Created = Get-ADComputer $Desktop.Hostedmachinename -Properties Created | ForEach-Object {$_.Created}
            ### If the AD user has never connected to the Citrix Desktop we will add Never Connected to the csv file for those fields
            If ($null -eq $Desktop.LastConnectionTime){
                $LastConnection = "Never Connected"
                $EndPoint = "Never Connected"
                $EndPoint_IP = "Never Connected"
            }
            Else{
                $LastConnection = $Desktop.LastConnectionTime
                $EndPoint = $Desktop.ClientName
                $EndPoint_IP = $Desktop.ClientAddress
            }
            #####################################################################
            ### Calling function to write to CSV
            Write_to_csv
            ### Cleaning up variables
            Clear-Variable VM, IP, SummaryState, LastConnection, Created, EndPoint, EndPoint_IP -ErrorAction SilentlyContinue
            #####################################################################
        }
        #####################################################################
    }
    #####################################################################
}
#####################################################################

That’s it for now, thanks for reading!


Check out my other blog posts:

Media Sync: Organize Your Photos and Videos with PowerShell

Do you have photos and videos that you have taken over the years that are scattered all over the place? Do you want to have all your photos and videos organized? Do you want all your photos and videos to have a standardized naming scheme? If you answered YES to these questions, then this is…

NetNeighbor Watch: The PowerShell Alternative To Arpwatch

In this post, we are going to setup NetNeighbor Watch on a Raspberry Pi. NetNeighbor Watch can keep an eye on your network and send you an email when a new host is discovered. NetNeighbor Watch is done completely in PowerShell. The results are very similar to those of arpwatch. NetNeighbor Watch is for anyone…

Creating a PowerShell Module to Improve Your Code

Do you have PowerShell code that you reuse in your scripts over and over? Do you have server names hard coded in variables? Are you using a text file or CSV file to import server names? Do you find yourself only utilizing one server out of a cluster of servers to make your PowerShell commands?…

Generate a DHCP Report Via PowerShell

Today we are going to look at how to generate a DHCP scope statistics HTML report by using PowerShell. This report will give you one location to see all of your DHCP scope statistics across all of your Windows DHCP servers. It will dynamically pull the DHCP servers and associated DHCP scopes within your Active…

Increase VMware VM Disk Size with PowerShell

In this post, I will cover how to use PowerShell to increase the disk size of a Windows 10 VM running in a VMware vCenter environment. Automating simple but time consuming tasks like this can save precious time for your support staff or yourself. There are two parts to accomplishing this task; first we need…

Manage Citrix Tags with PowerShell

In this post, we are going to cover how to manage Citrix tags with PowerShell. Citrix Studio is a great tool, but it can be very time consuming especially if you have to do bulk tag actions. Citrix tags can be used in several methods, but this post is focused on desktop tagging. This post…

Create a Text Box to Accept User Input for PowerShell GUI

Do you have a PowerShell GUI that needs to accept text input? Maybe you want to be able to copy and paste a list of computers, ip addresses, user names or some other data. I will show you how to add a text box into your PowerShell GUI to handle that input. If you haven’t…

Utilizing PowerShell Out-GridView as a GUI Alternative

In my previous post, I showed how you can build a simple menu driven PowerShell GUI; check it out here. To further improve upon that simple GUI, we will go over how to use Out-GridView. Out-GridView is a built-in powershell cmdlet that sends the output from a given command to an interactive window. This post…

How to Create a Simple PowerShell GUI

Have you ever wanted to create a menu driven PowerShell GUI to conduct a series of automated tasks? You are in luck, because that is exactly what we are going to cover in this post. This very simple PowerShell GUI will help you easily distribute your code to other team members or maybe you just…

Leave a comment