Delete local user profiles across multiple servers

Posted by

If you are using Remote Desktop / Citrix services you may, like me, find a need to delete locally cached profiles of certain users across the RDP farm. This is fine to do manually if you just have one or two servers, but when you have several session host servers and it can become a bit time consuming.

 

I’ve compiled a script that will work through a list of servers (2008 and above) and query for all the locally cached profiles, you then select the user from the list and will go through each server manually and delete the profile from the server.

 

It’s PowerShell based and uses WMI, so you need to make sure you have the relevant remote access rights on the servers you are querying, and the rights to delete profiles.

 

You will need to put the names of the servers and the path to the log file in the script to meet your requirements.

 

image

 

   1: #GUI interface to delete user profiles from remote desktop session host server

   2:  

   3: #Setup script variables

   4: #add computers to computers variable to search for profiles on those computers

   5: [array] $Computers = "sv-rdsh01","sv-rdsh02","sv-rdsh03","sv-rdsh04","sv-rdsh05"

   6: $log = "\\astonmartin.int\services$\LogFiles\RDP\profile.txt"

   7: $date = Get-Date

   8:  

   9: #Reset variables

  10: $selecteduser = ""

  11: $profilelist = @()

  12:  

  13:  

  14:  

  15: #Start the form

  16: SetupForm ;

  17: 

  18: 

  19: Function SetupForm {

  20:     #Setup the form

  21:     [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")

  22:     [void] [System.Reflection.Assembly]::LoadWithPartialName("System.Drawing") 

  23:  

  24:     $objForm = New-Object System.Windows.Forms.Form 

  25:     $objForm.Text = "Select user(s)"

  26:     $objForm.Size = New-Object System.Drawing.Size(300,320) 

  27:     $objForm.StartPosition = "CenterScreen"

  28:  

  29:     $btnDelete = New-Object System.Windows.Forms.Button

  30:     $btnDelete.Location = New-Object System.Drawing.Size(120,240)

  31:     $btnDelete.Size = New-Object System.Drawing.Size(75,23)

  32:     $btnDelete.Text = "Delete Profile"

  33:     $objForm.Controls.Add($btnDelete)

  34:     

  35:     #When a user clicks the log off button get the details of the logged in users and call the scriptactions function

  36:     $btnDelete.Add_Click(

  37:        {

  38:        #set the selecteduser variable to be that of the user selected in the form

  39:     $selecteduser = $objCombo.SelectedItem

  40:                DeleteProfile

  41:             #$objForm.Close()

  42:         

  43:          })

  44:  

  45:     $CancelButton = New-Object System.Windows.Forms.Button

  46:     $CancelButton.Location = New-Object System.Drawing.Size(200,240)

  47:     $CancelButton.Size = New-Object System.Drawing.Size(75,23)

  48:     $CancelButton.Text = "Cancel"

  49:     $CancelButton.Add_Click({$objForm.Close()})

  50:     $objForm.Controls.Add($CancelButton)

  51:  

  52:     $objLabel = New-Object System.Windows.Forms.Label

  53:     $objLabel.Location = New-Object System.Drawing.Size(10,20) 

  54:     $objLabel.Size = New-Object System.Drawing.Size(280,20) 

  55:     $objLabel.Text = "Please select user to delete profile:"

  56:     $objForm.Controls.Add($objLabel) 

  57:  

  58:     $objCombo = New-Object System.Windows.Forms.ComboBox

  59:     $objCombo.Location = New-Object System.Drawing.Size(10,40) 

  60:     $objCombo.Size = New-Object System.Drawing.Size(260,20) 

  61:     $objCombo.Height = 80

  62:     

  63:     

  64:  

  65: #Run through each computer in the computers variable to compile a list of unique user accounts across all servers

  66: ForEach ($computer in $Computers) {

  67:  

  68: #use WMI to find all users with a profile on the servers

  69: Try{

  70:     [array]$users = Get-WmiObject -ComputerName $computer Win32_UserProfile -filter "LocalPath Like 'C:\\Users\\%'" -ea stop  

  71:     }

  72: Catch {  

  73:     Write-Warning "$($error[0]) "  

  74:     Break  

  75:     } 

  76:  

  77: #compile the profile list and remove the path prefix leaving just the usernames

  78: $profilelist = $profilelist + $users.localpath -replace "C:\\users\\"

  79:  

  80: #filter the user names to show only unique values left to prevent duplicates from profile existing on multiple computers

  81: $uniqueusers = $profilelist | Select-Object -Unique

  82: }

  83:  

  84: #adds the unique users to the combo box

  85: ForEach($user in $uniqueusers) {

  86:     [void] $objCombo.Items.Add($user)

  87: }

  88:     

  89:     $objForm.Controls.Add($objCombo) 

  90:  

  91:     $objForm.Topmost = $True

  92:  

  93:     $objForm.Add_Shown({$objForm.Activate()})

  94:     [void] $objForm.ShowDialog()

  95: }

  96:  

  97:  

  98: Function DeleteProfile {

  99: #Add the path prefix back to the selected user

 100: $selectedUser = "C:\Users\$selecteduser"

 101:  

 102: #This section reads through all the computers and deletes the profile from all the computers - it catches any errors. 

 103: ForEach ($computer in $Computers) {

 104:     Try {

 105:         (Get-WmiObject -ComputerName $computer Win32_UserProfile | Where {$_.LocalPath -eq $selecteduser}).Delete()

 106:         Write-Host -ForegroundColor Green "$selecteduser has been deleted from $computer"

 107:         Add-Content $log "$date $selecteduser profile has been deleted from $computer"

 108:         }

 109: Catch [System.Management.Automation.MethodInvocationException]{

 110:     Write-Host -ForegroundColor Red "ERROR: Profile is currently locked on $computer - please use log off user script first"

 111:     Add-Content $log "$date $selecteduser Profile is currently locked on $computer - please use log off user script first"

 112:     }

 113:  

 114: Catch [System.Management.Automation.RuntimeException] {

 115:     Write-Host -ForegroundColor Yellow "INFO: $selecteduser Profile does not exist on $computer"

 116:     Add-Content $log "$date INFO: $selecteduser Profile does not exist on $computer"

 117:     }

 118:  

 119: Catch {

 120:     Write-Host -ForegroundColor Red "ERROR: an unknown error occoured. The error response was $error[0]"

 121:     Add-Content $log "$date ERROR: an unknown error occoured. The error response was $error[0]"

 122:     }

 123:     }

 124:     

 125:  

 126:  

 127:  

 128: #Add a label to say process is complete

 129:     $objLabel1 = New-Object System.Windows.Forms.Label

 130:     $objLabel1.Location = New-Object System.Drawing.Size(10,100) 

 131:     $objLabel1.Size = New-Object System.Drawing.Size(280,20) 

 132:     $objLabel1.Text = "Deletion complete, check log for more details."

 133:     $objForm.Controls.Add($objLabel1) 

 134:  

 135: #Add a view log button to view the log file

 136:     $LogButton = New-Object System.Windows.Forms.Button

 137:     $LogButton.Location = New-Object System.Drawing.Size(50,150)

 138:     $LogButton.Size = New-Object System.Drawing.Size(75,23)

 139:     $LogButton.Text = "View Log"

 140:     $LogButton.Add_Click({Invoke-Item $log})

 141:     $objForm.Controls.Add($LogButton)

 142: }

 143:  

 144:  

 145: #Setup script variables

 146: #add computers to computers variable to search for profiles on those computers

 147: [array] $Computers = "server1","server2","server3","server4","server5"

 148: $log = "\\server\share\logfile.txt"

 149: $date = Get-Date

 150:  

 151: #Reset variables

 152: $selecteduser = ""

 153: $profilelist = @()

 154:  

 155:  

 156:  

 157: #Start the form

 158: SetupForm