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.
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.
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 profil folders in C:\Users using a simple script from the article Getting Directory Sizes in 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.
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).
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 or with a PoSh script in Task Scheduler.
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()}
16 comments
Using Remove-WmiObject to get rid of the Win32_UserProfile sets only clears out the user home folders. I found that users can still sign in with their old locally-cached Windows Hello (for Business) PINs; is there a way to flush those credentials out of the TPM too?
This article shows how to clear user profiles that have not been logged in for a long time. After deleting the profile, users can log in again and a new profiles directory will be created for them.
Amazing! Thanks a lot for this!
This is going to save me so much time.. and its so clearly explained.. Fantastisch!
Thank you for the very detailed article. However, what is the right command to delete only a specific user profile?
I use the below command STE.
Get-CimInstance -Class Win32_UserProfile | Where-Object { $_.LocalPath.split(‘\’)[-1] -eq ‘UserA’ } | Remove-CimInstance
Hi. I tried run the script to delete old profiles in my Windows Server 2016 but the message was showed:
Exception calling “ConvertToDateTime” with “1” argument(s): “Exception calling “ToDateTime” with “1” argument(s): “Specified argument was out of the range of
valid values.
Parameter name: dmtfDate””
At line:3 char:64
+ … le | Where {(!$_.Special) -and (!$_.Loaded) -and ($_.ConvertToDateTim …
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : ScriptMethodRuntimeException
Were you able to figure out how to fix this error? I am getting it as well
yeah commun error, due to an update. i am looking for the solution as well for a fair amount of time
Instead of « $_.ConvertToDateTime($_.LastUseTime) » just use « $_.LastUseTime »
i applied this policy and it ended up deleting 2 profiles that werent older than the setting in the GPO was set to. Now trying to use data recovery software on the drives and that’s only pulling corrupted files now. Wish i would of never even used the GPO. Not sure what the hell happened.
I started a few computers and none of the user profiles are older that one day. The computers has been shutdown over a month and at least 10 user profiles are older than two years.
PS C:\WINDOWS\system32> Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-1))}| Measure-Object
Count : 0
Average :
Sum :
Maximum :
Minimum :
Property :
That GPO doesn’t work either because Windows updates modifies NTUSER.DAT file in every user profile. New modified times are also show in Advanced System Settings -> User Profiles -> Settings.
[…] to work around the issue The PowerShell script in the Windows OSHub post “How to Delete Old User Profiles Using GPO and PowerShell?” looks […]
Update: July 2021
Reportedly, the group policy setting now checks the LocalProfileUnloadTimeLow & LocalProfileUnloadTimeHigh keys within HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\, to determine when to delete a profile.
Hi Gents, I have a question with regard to the scripts above. I have an environment running RDS sessions and want to create a script too clear all inactive user profiles older that 60days. Now my problem is I dont want to delete the profiles like Public and Remote User and Admin and cscsa user profiles. How do I go about altering this script to work for me. I am not a powershell guru so any assistance would be appreciated.
Hi Valentino,
Please check the following line in this script:
$ExcludedUsers =”Public”,”zabbix_agent”,”svc”,”user_1”,”user_2”
It contains accounts that the script will not delete. Just add your accounts to it.