Group PoliciesPowershell

How to Delete Old User Profiles Using GPO and PowerShell

By April 20, 2020No Comments

On Windows workstations and servers, especially on RDS (Remote Desktop Services) servers, there is a regular need to remove old (unused) user profiles from C:\Users. The main problem of any Remote Desktop server is the constant growth the size of user profile directories on a local drive. It is partially solved by enabling quotas on the maximum user profiles size (with FSRM or NTFS quotas). However, if there are a lot of terminal server users, with time the C:\Users directory will accumulate a huge number of directories with user profiles that are not longer needed.

How to Delete a User Profile Manually in Windows?

Many novice Windows admins try to manually delete a user profile folder from C:\Users. You can do it if after manually deleting the folder, you will delete the user profile section with the link to this folder from the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\ CurrentVersion\ProfileList.

The correct way to manually delete a user profile in Windows is to open System Properties, go to Advanced System Settings -> User Profiles -> Settings, select a user in the list (the Size column shown the size of the profile on the local drive) and click the Delete button.

removing user profile manually in windows

But this is a manual method, and you may want to automate it.

GPO: Delete User Profiles Older Than a Specified Number Days

In Windows, there is a built-in Group Policy to automatically delete user profiles older than xx days. You can find the policy Delete user profiles older than a specified number days on system restart in the GPO section Computer Configuration -> Administrative Templates -> System -> User Profiles. You can enable this policy in the Local Group Policy Editor (gpedit.msc) or using domain policies in GPMC.msc.

Enable the policy and specify the number of days a user profile is considered active. When this period is over, Windows user profile service will automatically delete the profile at the next restart. It is recommended to specify the period of 45-90 days here.

group policy: Delete user profiles older than a specified number days on system restart

When using this policy, make sure that when a server is shut down or restarted there are no problems with the system time. Otherwise active user profiles may be deleted.

The main troubles associated with this automatic method of profile removal is waiting for the server restart and non-selectivity (you cannot prohibit deleting certain user profiles like local accounts, administrative accounts, etc.). Also, this policy may not work if some third-party software (most often it is an antivirus) accesses NTUSER.DAT file in user profiles and updates the date of last use.

PowerShell Script to Delete Old User Profiles in Windows

Instead of using the automatic cleanup profile policy described above, you can use a simple PowerShell script to find and remove profiles of disabled or inactive users.

First of all, let’s try to count the size of all user profile folders in C:\Users with powershell.

gci -force 'C:\Users'-ErrorAction SilentlyContinue | ? { $_ -is [io.directoryinfo] } | % {
$len = 0
gci -recurse -force $_.fullname -ErrorAction SilentlyContinue | % { $len += $_.length }
$_.fullname, '{0:N2} GB' -f ($len / 1Gb)
$sum = $sum + $len
}
“Total size of profiles”,'{0:N2} GB' -f ($sum / 1Gb)

The total size of all user profiles in C:\Users is 31,5 GB.

count the total user profile size on RDS host

Let’s display the list of users, whose profiles has not been used for more than 60 days. To find them, you can use the value in the LastUseTime field of the profile.

Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-60))}| Measure-Object

It turned out that I had 127 inactive user accounts on my RDS host (with a profiles total size of about 18 GB).

get inactive users list by profile LastUseTime on RDSH

To remove all these profiles, it is enough to redirect the user list to the Remove-WmiObject command (prior to running the script, it is recommended to double-check its output using the –WhatIf parameter):

Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and (!$_.Loaded) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-30))} | Remove-WmiObject –WhatIf

In order not to delete profiles of some users, like System and Network Service accounts, a local administrator account, accounts of users having active sessions, account exception list), you can modify the script as follows:

#The list of accounts, which profiles must not be deleted
$ExcludedUsers ="Public","zabbix_agent","svc",”user_1”,”user_2”
$LocalProfiles=Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and (!$_.Loaded) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-60))}
foreach ($LocalProfile in $LocalProfiles)
{
if (!($ExcludedUsers -like $LocalProfile.LocalPath.Replace("C:\Users\","")))
{
$LocalProfile | Remove-WmiObject
Write-host $LocalProfile.LocalPath, "profile deleted” -ForegroundColor Magenta
}
}

You can run this PowerShell script using a GPO at shutdown. Before configuring automatic deletion of profiles, it is recommended to test the script in your environment!

You can modify the script to automatically remove all user profiles added to the specific AD group (for example, DisabledUsers group):

$users = Get-ADGroupMember -Identity DisabledUsers | Foreach {$_.Sid.Value}
$profiles = Get-WmiObject Win32_UserProfile
$profiles | Where {$users -eq $_.Sid} | Foreach {$_.Delete()}

Leave a Reply