Powershell: The Ultimate User Deletion Script?

akkyu

Intro

I’ve spent the better part of 4 days working on a monster script. I needed this script to perform the following actions:

  1. Take a list of usernames and ensure they exist in Active Directory (AD).
  2. Take a list of usernames and disable their AD accounts
  3. Take a list of usernames and delete their AD accounts
  4. Take a list of usernames and delete any files in their User profile and RDS profile
  5. Take a list of usernames and delete any local profiles present on XenApp Servers
  6. Take a list of usernames and delete any of their assigned XenDesktops
  7. Take a list of usernames and remove their account from any published application
  8. Review this list of usernames and remove any accounts that should not be removed
  9. Provide feedback with write-verbose messages and create a log file of any actions

Needless to say, this script can easily be broken down into different functions for reusability, but I wanted an all-in-one script that would be used by other support team members. This script assumes PowerShell 2.0 and that the following cmdlets are available:

  • Microsoft Active Directory
  • Citrix XenApp Commands (SDK for XenApp 6 or 6.5 so you can run remote commands)
  • Citrix XenDesktop Commands

The user who runs the script should be a AD domain , XenApp and XenDesktop admin.  I do not recommend using this script as is, but you may find parts you can use in your environment.  Also review the URL’s in the .LINK section. I used ideas and code from these web pages to help write this script.

You can edit the #CONSTANTS section for your environment.

The Script

<#
.SYNOPSIS
	Takes ad username or object of usernames and deletes user's resources and account from the domain and Citrix environment.
.DESCRIPTION
	Removes a user's account and resources from the AD domain and Citrix environment.

	It is recommended that this script be run as an admin. In addition, the Microsoft Active Directory, XenDesktop, XenApp Powershell Cmdlets must be available for user and desktop deletion.
.PARAMETER username
      Required parameter.
      User account(s) that will be deleted. User accounts must be disabled before deletion. See disable parameter.
.PARAMETER disable
    Optional switch parameter.
    Defaults to $false.
    If present, user accounts will just be disabled.
.EXAMPLE
	PS C:\PSScript > .\delete-citrixuser.ps1 -username "someuser"

	Will use all default values.
    No feedback messages will be shown.
    All user accounts are expected to be already disabled otherwise, no accounts will be deleted.
.EXAMPLE
	PS C:\PSScript > .\delete-citrixuser.ps1 -username "someuser" -verbose

	Will use all default values.
    Feedback/progress messages will be shown.
    All user accounts are expected to be disabled otherwise, no accounts will be deleted.
.EXAMPLE
    PS C:\PSScript > .\delete-citrixuser.ps1 -username "someuser" -disable -verbose

    Will use all default values.
	Will set AD user accounts to disabled.
    Feedback/progress messages will be shown.
.INPUTS
	Username or object of usernames.
.OUTPUTS
	To see feedback messages use the -verbose common parameter. No objects are output from this script.  This script creates a user deletion log.
.NOTES
	NAME: delete-citrixuser.ps1
	VERSION: 1.00
    CHANGE LOG - Version - When - What - Who
                 1.00 - 07/21/2014 - Initial script - Alain Assaf
	AUTHOR: Alain Assaf
	LASTEDIT: July 21, 2014
.LINK
    http://www.linkedin.com/in/alainassaf/
    http://wagthereal.com
    http://stackoverflow.com/questions/11605893/checking-for-the-existence-of-an-ad-object-how-do-i-avoid-an-ugly-error-message
    http://powershell.com/cs/blogs/tips/archive/2009/06/26/using-switch-parameters.aspx
    http://technet.microsoft.com/en-us/library/ee692802.aspx
    Checking  Setting Remote Desktop Services Profile Settings
    http://winpowershell.blogspot.com/2006/08/suppressing-output-using-out-null-and.html
    http://blogs.msdn.com/b/powershell/archive/2009/12/29/arguments-for-remote-commands.aspx
    
Powershell: Script to delete Windows User Profiles on Windows 7/Windows 2008 R2
http://stackoverflow.com/questions/12727388/wildcard-with-variable-in-get-aduser http://ss64.com/ps/do.html http://technet.microsoft.com/en-us/library/ee177002.aspx http://technet.microsoft.com/en-us/library/ee176955.aspx http://support.citrix.com/static/kc/CTX127254/help/ http://social.technet.microsoft.com/Forums/windowsserver/en-US/79f9be3c-2945-471d-8a60-a1390f376d6e/removeaduser-has-no-force-option?forum=winserverpowershell http://titlerequired.com/2012/10/29/powershell-make-it-do-something-useful/ #> Param( [parameter(Position = 0, Mandatory=$True )] [ValidateNotNullOrEmpty()] $username, [parameter(Position = 1, Mandatory=$False )] [ValidateNotNullOrEmpty()] [switch]$disable ) ### FUNCTION: get-mymodule ##################################################### Function Get-MyModule { Param([string]$name) if(-not(Get-Module -name $name)) { if(Get-Module -ListAvailable | Where-Object { $_.name -eq $name }) { Import-Module -Name $name $true } #end if module available then import else { $false } #module not available } # end if not module else { $true } #module already loaded } ### FUNCTION: get-mymodule ##################################################### ### FUNCTION: get-mysnapin ##################################################### Function Get-MySnapin { Param([string]$name) if(-not(Get-PSSnapin -name $name)) { if(Get-PSSnapin -Registered | Where-Object { $_.name -eq $name }) { add-PSSnapin -Name $name $true } #end if module available then import else { $false } #snapin not available } # end if not snapin else { $true } #snapin already loaded } ### FUNCTION: get-mysnapin ##################################################### ### FUNCTION: Check-ADUser ##################################################### #Function to check if user account exists in AD Function Check-ADUser { Param ($usrname) $isuser = $(try {Get-ADUser $usrname} catch {$null}) if ($isuser -ne $null) { return $true } else { return $false } } ### FUNCTION: Check-ADUser ##################################################### ### FUNCTION: Remove-UserProfile################################################ Function Remove-UserProfile { param( [parameter(ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [ValidateNotNullOrEmpty()] [string[]]$ComputerName = $env:computername, [parameter(mandatory=$true)] [ValidateNotNullOrEmpty()] [string]$UserName, [parameter(mandatory=$false)] [ValidateNotNullOrEmpty()] [string]$localpath ) foreach($Computer in $ComputerName) { Write-Verbose "Looking for local profiles on $Computer" if(Test-Connection -ComputerName $Computer -Count 1 -ea 0) { $UserProfile = Get-WmiObject Win32_UserProfile -Computer $Computer -ea 0 -filter “localpath='$localpath'” if (!$UserProfile) { write-Verbose “$Username not found on $Computer” } else { $UserProfile | Remove-WmiObject Write-Verbose "$UserName profile deleted successfully on $Computer" } } else { write-verbose "Cannot connect to $Computer" } } } ### FUNCTION: Remove-UserProfile################################################ #Constants $datetime = get-date -format "MM-dd-yyyy_HH-mm" $Domain=(Get-WmiObject Win32_ComputerSystem).Domain $UserOU="OU=Users,DC=domain,DC=local" $CloudProfilePath = "\\userprofileserver.domain.local\upm\" $CloudRDSProfilePath = "\\userprofileserver.domain.local\profiles\" $ScriptRunner = (get-aduser $env:username | select name).name $XAZDC = "XenAppZDC.domain.local" $PSModules = ("activedirectory") $PSSnapins = ( "Citrix.ADIdentity.Admin.V1", "Citrix.Broker.Admin.V1", "Citrix.Common.Commands", "Citrix.Common.GroupPolicy", "Citrix.Configuration.Admin.V1", "Citrix.Host.Admin.V1", "Citrix.LicensingConfig.Admin.V1", "Citrix.MachineCreation.Admin.V1", "Citrix.MachineIdentity.Admin.V1", "Citrix.XenApp.Commands") $accountnames = New-Object System.Collections.ArrayList #Any account in DONOTDELETE array will always be removed from accountnames array and will not be disabled or deleted. $DONOTDELETE = New-Object System.Collections.ArrayList $DONOTDELETE.Add("Guest")>$null $DONOTDELETE.Add("krbtgt")>$null #Get List of XenApp Servers $XAServers = get-xaserver -ComputerName $XAZDC | select servername #Populate accountnames foreach ($a in $username) {$accountnames.Add($a)>$null} #Remove any users in accountnames who are in the DONOTDELETE array foreach ($b in $DONOTDELETE) {$accountnames.remove($b)>$null} #initalize output array $finalout = @() #Import Module(s) and Snapin(s) foreach ($module in $PSModules) { if (!(get-mymodule $module)) { write-verbose "$module PowerShell Cmdlet n ot available." write-verbose "Please run this script from a system with the $module PowerShell Cmdlets installed." exit } } foreach ($snapin in $PSSnapins) { if (!(get-MySnapin $snapin)) { write-verbose "$snapin PowerShell Cmdlet not available." write-verbose "Please run this script from a system with the $snapin PowerShell Cmdlets installed." exit } } #Confirm OU is valid If (!([adsi]::Exists("LDAP://$UserOU"))) { write-verbose "$UserOU IS NOT VALID" write-verbose "Please use the following format: OU=Users,DC=domain,DC=local" Exit } #Confirm user(s) exists foreach ($usr in $accountnames) { $UserStatus = Check-ADUser $usr If ($UserStatus) { #User Exists - delete or disable user write-verbose "USER EXISTS: $usr" If ($disable) { #Disable is true - just disable the user write-verbose "DISABLING: $usr" disable-ADAccount -Identity $usr get-aduser $usr -properties info | foreach { Set-ADUser -Identity $_.samaccountname -Replace @{info="$($_.info)`r`nAccount disabled on: $datetime by $ScriptRunner"} } $finalout += "$usr disabled by $ScriptRunner on $datetime" } Else { #Disable flag is false - delete user (if already disabled) #Confirm user account is disabled $citrixuser = Get-ADUser $usr -properties profilepath | select -ExcludeProperty disting* if ($citrixuser.Enabled) { #User account is not disabled - write error and go to next user write-verbose "$usr is not disabled. Please disable prior to deletion." $finalout += "$usr is not disabled. Please disable prior to deletion." } else { #user account is disabled clean up and delete user account. #Get User's profile and RDS profile path $userprofilepath = $citrixuser.profilepath $citrixuser = [ADSI]“LDAP://$citrixuser” $rdsprofilepath = $citrixuser.psbase.InvokeGet(“terminalservicesprofilepath”) #Delete user's profile and RDS profile if (test-path $userprofilepath) { remove-item -Recurse -Force $userprofilepath write-verbose "FOLDER DELETED: $userprofilepath" $finalout += "FOLDER DELETED: $userprofilepath" } elseif (test-path ($userprofilepath + ".V2")) { remove-item -Recurse -Force ($userprofilepath + ".V2") write-verbose ("FOLDER DELETED: $userprofilepath" + ".V2") $finalout += ("FOLDER DELETED: $userprofilepath" + ".V2") } else { write-verbose ("FOLDER DOES NOT EXIST: $userprofilepath or $userprofilepath" + ".V2") } if (test-path $rdsprofilepath) { remove-item -Recurse -Force $rdsprofilepath write-verbose "FOLDER DELETED: $rdsprofilepath" $finalout += "FOLDER DELETED: $rdsprofilepath" } elseif (test-path ($rdsprofilepath + ".V2")) { remove-item -Recurse -Force ($rdsprofilepath + ".V2") write-verbose ("FOLDER DELETED: $rdsprofilepath" + ".V2") $finalout += ("FOLDER DELETED: $rdsprofilepath" + ".V2") } else { write-verbose ("FOLDER DOES NOT EXIST: $rdsprofilepath or $rdsprofilepath" + ".V2") } #Delete user's local profile on XenApp servers foreach ($srv in $XAServers) { $tmpdir = invoke-command -computername $srv.servername {"$env:homedrive\users"} $usrdir = $tmpdir + "\" + $usr remove-userprofile -computername $srv.servername -username $usr -localpath $usrdir.Replace("\","\\") -verbose } #Delete any assigned XenDesktops $user = $Domain.split(".")[0] + "\" + $usr $UserDesktops = Get-BrokerDesktop -AssociatedUserName $user -DesktopKind "Private" if ($UserDesktops -ne $null) { foreach ($d in $UserDesktops) { set-brokerprivatedesktop $d.machinename -InMaintenanceMode $true do { start-sleep -s 5 $result1 = Get-BrokerPrivateDesktop -MachineName $d.Machinename } until ($result1.InMaintenanceMode -eq $true) write-verbose "$d.machinename - maintenance mode enabled" New-BrokerHostingPowerAction -Action 'Shutdown' -MachineName $d.machinename do { start-sleep -s 5 $result2 = Get-BrokerHostingPowerAction -MachineName $d.MachineName | select -last 1 } until ($result2.State -eq "Completed") write-verbose "$d.machinename - powered down" } #Unassign user from desktop Remove-BrokerUser -Name $user -Machine $d.machinename #Remove desktop from DesktopGroup remove-Brokermachine -Machine $d.machinename -desktopgroup $d.desktopgroupuid #Delete desktop and AD Computer Account Remove-BrokerMachine -MachineName $d.machinename Remove-AcctADAccount -IdentityPoolName $d.Catalogname -ADAccountName $d.machinename -RemovalOption 'Delete' write-verbose "$d.machinename deleted from Desktop Studio and Active Directory" $finalout += "$d.machinename deleted from Desktop Studio and Active Directory" } else { write-verbose "No desktops associated with $user" $finalout += "No desktops associated with $user" } #Remove user from any Citrix published resources $apps = (Get-XAApplicationReport -ComputerName $XAZDC -BrowserName * | where {$_.Accounts -contains $user}) if ([bool]($apps -ne $null)) { #Found useraccount assigned to some published resources foreach ($app in $apps) { Remove-XAApplicationAccount -ComputerName $xazdc -BrowserName $app.Browsername -Accounts $user } } else { write-verbose "$usr is not assigned to any published applications" } #Delete user account from AD remove-aduser -identity $usr -confirm:$false write-verbose "$user removed from $domain" $finalout += "$user removed from $domain" } } } Else { #User does not exist - stop and go to next user write-verbose "$usr does not exist." } } #CreateReport if ($finalout -ne $null) { $LogFileFolder = "c:\_scripts" # $datetime = get-date -format "MM-dd-yyyy_HH-mm" $LogFileName = "Citrix_DeleteUser_Report" + $datetime + ".txt" $LogFile = $LogFileFolder + "\" + $LogFilename $finalout | ft -auto | out-file $Logfile -append }

I look forward to any and all comments. I’m sure there are better ways to write some of the above. There’s always more than one way to skin a cat in PowerShell.

Thanks,
Alain

PowerShell: E-mail Server Load and Assigned LE

NOTE: I recently updated this script for XenApp 7.x environments.
Note: I wrote this script for a XenApp 5.0 environment with PowerShell 2.0 and CTP3 XenApp Cmdlets.

Update: I found that the simple ping used with Test-Connection PowerShell cmdlet was not identifying servers that were not available for login. In our environment we occasionally have a server come back from a scheduled reboot and ping, but not allow RDP/ICA connections. I’ve incorporated a Test-Port function from Aaron Wurthmann’s PowerShell Script Connect-RDP.ps1

Intro

Citrix has utilized load balancing in XenApp for years. This feature allows you to set parameters in a load evaluator, apply it to a server, and have Citrix manage how users are assigned to servers. Despite the numerous tools available on the market that report on your XenApp farm utilization, Citrix user load balancing remains the easiest to implement (and cheapest). That being said, beyond running the “qfarm /load” command over and over, it’s difficult to report on your farm’s load. Thankfully, with the Citrix PowerShell commands we can regularly report on a server’s load (including the load evaluator rules) and e-mail that information.

The Script

###############################################################################
## Title : get-ctxLoadAndLE.ps1
## Description : Gathers server load, assigned LE, and active and disconnected sessions
## Author : Alain Assaf
## Date : 01/11/2012
## Sources : Logging and e-mail sections from Raido
## Sources : http://powershell.com/cs/blogs/ebook/archive/2008/10/23/chapter-4-arrays-and-hashtables.aspx
## Sources : http://technet.microsoft.com/en-us/library/ff730946.aspx
## Sources : http://technet.microsoft.com/en-us/library/ff730936.aspx
## Sources : [test-port function] http://irl33t.com/blog/2011/03/powershell-script-connect-rdp-ps1
## Notes : Assumes Citrix PowerShell cmdlets are installed
#### Changelog ################################################################
# -When - What - Who
# -01/11/2012 -Initial script -Alain Assaf/Chemtura
# -01/18/2012 - Changed way I get user sessions because it was timing out
# -02/20/2012 - Added sendTo variable to add mulitple receipients
# -03/05/2012 - Added lines to include LE Rules
# -04/26/2012 - Added Test-Port function from Aaron Wurthmann (aaron (AT) wurthmann (DOT) com)
###############################################################################

# Test-Port function to test RDP availability
# Written by Aaron Wurthmann (aaron (AT) wurthmann (DOT) com)
function Test-Port{
 Param([string]$srv=$strhost,$port=3389,$timeout=300)
 $ErrorActionPreference = "SilentlyContinue"
 $tcpclient = new-Object system.Net.Sockets.TcpClient
 $iar = $tcpclient.BeginConnect($srv,$port,$null,$null)
 $wait = $iar.AsyncWaitHandle.WaitOne($timeout,$false)
 if(!$wait)
 {
 $tcpclient.Close()
 Return $false
 }
 else
 {
 $error.Clear()
 $tcpclient.EndConnect($iar) | out-Null
 Return $true
 $tcpclient.Close()
 }
}
#Assign e-mail(s) to $sendto variable and SMTP server to $SMTPsrv
$sendto = SOMEONE@YOURDOMAIN.com
$SMTPsrv = 127.0.0.1

#Initialize array
$finalout = @()

#add XenApp Cmdlets
Add-PSSnapin -name *citrix* -erroraction silentlycontinue

#Get server list
$ctxservers = get-xaserver | select -Property servername | sort -Property servername

#Get user sessions
$ctxsrvSessions = Get-XASession | select -property SessionID,ServerName,AccountName,Protocol,State| where {($_.State -eq 'Active' -or $_.State -eq 'Disconnected') -and $_.Protocol -eq 'Ica'}

#Create a new object array with all the data we need
foreach ($srv in $ctxservers) {
 $ctxsrv = $srv.servername
 if (test-port $ctxsrv) {
  $srvload = get-xaserverload -servername $ctxsrv | select -Property ServerName,LoadEvaluatorName,Load,Rules
  $srvactive = @($ctxsrvSessions | where {$_.Servername -eq $ctxsrv -and $_.State -eq 'Active'}).count
  $srvdisconn = @($ctxsrvSessions | where {$_.Servername -eq $ctxsrv -and $_.State -eq 'Disconnected'}).count
  $objctxsrv = new-object System.Object
  $objctxsrv | Add-Member -type NoteProperty -name ServerName -value $srvload.servername
  $objctxsrv | Add-Member -type NoteProperty -name LEName -value $srvload.LoadEvaluatorName
  $objctxsrv | Add-Member -type NoteProperty -name Load -value $srvload.Load
  $objctxsrv | Add-Member -type NoteProperty -name Active -value $srvactive
  $objctxsrv | Add-Member -type NoteProperty -name Disconnected -value $srvdisconn
  $objctxsrv | Add-Member -type NoteProperty -name 'Server User Load' -value $srvload.Rules[0].Load.ToString()
  $objctxsrv | Add-Member -type NoteProperty -name 'Load Throttling' -value $srvload.Rules[1].Load.ToString()
  $objctxsrv | Add-Member -type NoteProperty -name 'Memory Usage' -value $srvload.Rules[2].Load.ToString()
  $objctxsrv | Add-Member -type NoteProperty -name 'CPU Usage' -value $srvload.Rules[3].Load.ToString()
  $finalout += $objctxsrv
 } else {
  $objctxsrv = new-object System.Object
  $objctxsrv | Add-Member -type NoteProperty -name ServerName -value $ctxsrv
  $objctxsrv | Add-Member -type NoteProperty -name LEName -value "OFFLINE"
  #$objctxsrv | Add-Member -type NoteProperty -name Load -value "0"
  #$objctxsrv | Add-Member -type NoteProperty -name Active -value "0"
  #$objctxsrv | Add-Member -type NoteProperty -name Disconnected -value "0"
  $finalout += $objctxsrv
 }
}

#Create HTML Header
$head = '<style>
META{http-equiv:refresh content:30;}
BODY{font-family:Verdana;}
TABLE{border-width: 1px;border-style: solid;border-color: black;border-collapse: collapse;}
TH{font-size:12px; border-width: 1px;padding: 2px;border-style: solid;border-color: black;background-color:Aquamarine}
TD{font-size:12px; border-width: 1px;padding: 2px;border-style: solid;border-color: black;background-color:GhostWhite}
</style>
<script type="text/javascript">
$(function(){
var linhas = $("table tr");
$(linhas).each(function(){
var Valor = $(this).find("td:first").html();
if(Valor == "OFFLINE"){
  $(this).find("td").css("background-color","Red");
}else if(Valor == "Running"){
  $(this).find("td").css("background-color","Green");
}
});
});
</script>
'
#$header = "<H5>XenApp DASHBOARD</H5>"
$title = "XenApp DASHBOARD"
#$body = $finalout | ConvertTo-Html -head $head -body $header -title $title
#$body = $finalout | ConvertTo-Html -head $head -title $title

#Create mail parameters
$messageParameters = @{
 Subject = "Report: Server Load and Assigned LE for XenApp Farm"
 Body = $finalout | ConvertTo-Html -head $head -title $title | out-string
 From = "Admin-Citrixtask@chemtura.com"
 To = $sendto
 SmtpServer = "$SMTPsrv"
}

#Send e-mail with Server load data
Send-MailMessage @messageParameters -BodyAsHtml

#Uncomment to output the results in an HTA file to view in a browser
#$finalout | ConvertTo-Html -head $head -title $title | out-file $env:temp\report.hta
#ii $env:temp\report.hta

Example e-mail

Currently the script records the following:

  • Server Name
  • Assigned Load Evaluator
  • Server Load
  • Active Sessions
  • Disconnected Sessions
  • Load Evaluator Rules (you will have to modify the above script depending on which LE rules are using)
    • User Load
    • Load Throttling
    • Memory Usage
    • CPU Usage
image
click on image to make it larger

I created a repeating scheduled task on a XenApp server in the farm I wanted to report on. Now my team gets an hourly report of the farm load.

Explore more

Load Manager Values Explained

Load Manager Rules Explained

Performance Monitor Counters Used by Load Manager

Troubleshooting Load Balancing Issues

How to Enable the Load Manager Log for Load Balancing Issues

Conclusion

Please try it out and let me know how it works for you. As you can see above, I borrowed a javascript that was supposed to color the cells for an OFFLINE server red, but have not been able to get it to work. If anyone has a solution, I will be happy to update this post and spotlight you, your blog, or twitter account.

As always, I welcome all comments and questions.

Thanks,
Alain

EdgeSight: What’s the Point?

Intro

In my experience, EdgeSight is not typically deployed in XenApp environments. The reasons for this are many including, it seems redundant and overlaps with an already installed solution, it’s hard to use (sorry Citrix, this is a great tool, but it almost requires a dedicated resource to manage it), most XenApp administrators/managers do not know why or how it should be used, and finally you need a platinum license in order to run the agent in advanced mode which is a deal-breaker for some companies.

In this post, I will cover a use case for EdgeSight that measures memory utilization of IE after a registry change is made.

Why Internet Explorer will kill your memory

Trond Eirik Haavarstein at XenAppBlog wrote a 2-part series on running Internet Explorer 7 or 8 in a terminal server environment and its impact on memory utilization. He shows how memory is used by multiple tabs in IE and references an MSDN Blog about a registry change that will modify the Tab Process Growth.

Any XenApp/Terminal Services environment is an exercise in resource management so we wanted to apply this change in our staging environment (prior to applying it in production) to determine if it will have a positive impact.

EdgeSight – Oh I get it!

After applying the registry change via a GPO, we waited a few days in order to gather enough data in EdgeSight to make a before/after comparison. Now we will walk through selecting the report we need and running it with the correct criteria.

Log into EdgeSight and go to the Browse Tab to select the report we need:

image

Here’s what you see (click on the picture to make it bigger)…

image

What! 143 reports? How do I get what I need? Well, we need to report on the performance of Internet Explorer. EdgeSight refers to applications as processes.  These can be executables launched by users (published applications) or processes run by system accounts. Click on Process under the Object Type table. Now we see (click on the picture to make it bigger)…

image

We’ve filtered our report list to just 33 items. If we wish to further filter our results, we can select Historical for the time frame and Performance for Data Type which gives us 15 reports to look through.  Since we know we’re looking for a report related to memory, we could have just as easily typed “memory” in the search field which gives us the following…

image

Then click on Processes for the Object Type would give us just 3 results…

image

You can see that you can narrow your choices in a couple of different ways.  The report we need is the Process Memory Usage. Let’s click on this report and see what the default result is.

image

As you can see above, we are looking at the entire environment monitored by EdgeSight and looking at the top 20 processes for the past week. The resulting report is grouped by Process, then Device, and then user…

image

We need to narrow the parameters of this report to give us the info we need for Internet Explorer…

We can first choose to limit the Department to just our XenApp servers where we made the registry change. Next we can hit the Category drop down and select Web Browsers.

image

If you only have IE installed on your XenApp servers, this makes it easy. Otherwise, you will have to check the Optional Parameters and find the process in the Process Picker Window…

image

Type iexplore.exe into the Filter window, make sure the By File Name radio button is checked and click filter

image

Select it and click Ok. Now Internet Explorer is the selected process in the Optional Parameters section.

image

Clicking on Go will show us the top 20 instances of Internet Explorer’s usage of Virtual, Private, and Working Set memory over the period of 3/22 to 3/29. For a detailed explanation of memory, I would refer to Mark Russinovich’s blog post here.

image

We want to track the change in memory usage over time, so we have to modify how the data is grouped. Changing the grouping to Date, Process, and All gives us the following:

image

Now it’s a matter of selecting the correct dates and comparing the results to show how the change we made has positively impacted IE memory usage.

Here are the results I recorded with EdgeSight in our staging environment:

Before:

After:

image

I found that the IE memory footprint was reduced by 10,000 – 30,000 kb after applying the registry change. That works out to around 10-30 MB per user which isn’t too bad in shared environment.

Have you tried this registry change and if so what was your result? How did you measure it? Comment Below

Thanks,
Alain