How to Remove Windows User Profiles Remotely with PowerShell

Shared and hot-desk PCs build up old user profiles over time. Every person who has ever signed in leaves a folder in C:\Users, and on a small SSD that can quickly eat into free space. The usual fix is System Properties > Advanced > User Profiles, but that dialog only works on the computer you are sitting at. There is no option to point it at another machine.

Here is how to do the same job remotely, from your own PC, using PowerShell.

First, see which profiles are taking up space

Before deleting anything, it helps to know which profiles are worth removing. The command below reads each profile folder over the PC’s admin share (\\PC01\c$) and adds up its size. It does not need a remote session, just an account with admin rights on the PC. It skips OneDrive files that are stored online only, because these report their full size even though they use almost no disk space. Without that, someone with a large OneDrive could look like they are using tens of gigabytes when they are not. It can take a few minutes on large profiles, and the totals can be slightly low because locked files are skipped, but it is more than accurate enough to see where the space has gone.

$pc = 'PC01'
Get-ChildItem "\\$pc\c$\Users" -Directory | ForEach-Object {
    $files = Get-ChildItem $_.FullName -Recurse -Force -File -ErrorAction SilentlyContinue |
             Where-Object { -not ([int]$_.Attributes -band 0x401000) }
    [pscustomobject]@{
        Profile = $_.Name
        GB      = [math]::Round(($files | Measure-Object Length -Sum).Sum / 1GB, 2)
    }
} | Sort-Object GB -Descending | Format-Table -AutoSize

Why not just delete the folder?

It is tempting to browse to \\PC01\c$\Users and delete the folder. Don’t. Windows also keeps a record of each profile in the registry, under ProfileList. If you remove only the folder, that record stays behind, and the next time the user signs in, they get a temporary profile instead of a working one. The method below removes both the folder and the registry entry, exactly as the User Profiles dialog does.

Step 1: Connect to the remote PC

Windows client PCs usually don’t have WinRM (PowerShell remoting) switched on, so Get-CimInstance -ComputerName will often fail with a “cannot connect to the destination” error. Connecting over DCOM gets around this:

$s = New-CimSession -ComputerName PC01 -SessionOption (New-CimSessionOption -Protocol Dcom)

Run PowerShell as an account that is a local administrator on the target PC, such as a domain admin account.

Optional: check when each user last signed in

The LastUseTime value shown in Step 2 is not reliable on Windows 10 and 11, because background tasks update it. A better guide is the time Windows last loaded each profile, which is effectively the last time that person signed in to this PC. Windows keeps this in the registry, and you can read it over the same session:

$hklm = [uint32]2147483650
$key  = 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList'

Get-CimInstance -CimSession $s -ClassName Win32_UserProfile | Where-Object { -not $_.Special } | ForEach-Object {
    $sub = "$key\$($_.SID)"
    $hiArgs = @{hDefKey=$hklm; sSubKeyName=$sub; sValueName='LocalProfileLoadTimeHigh'}
    $loArgs = @{hDefKey=$hklm; sSubKeyName=$sub; sValueName='LocalProfileLoadTimeLow'}
    $hi = (Invoke-CimMethod -CimSession $s -Namespace root\default -ClassName StdRegProv -MethodName GetDWORDValue -Arguments $hiArgs).uValue
    $lo = (Invoke-CimMethod -CimSession $s -Namespace root\default -ClassName StdRegProv -MethodName GetDWORDValue -Arguments $loArgs).uValue
    $last = $null
    if ($null -ne $hi -and $null -ne $lo) { $last = [datetime]::FromFileTime(([int64]$hi -shl 32) -bor [int64]$lo) }
    [pscustomobject]@{ Profile = $_.LocalPath; LastLogon = $last; Loaded = $_.Loaded }
} | Sort-Object LastLogon | Format-Table -AutoSize

The oldest sign-ins appear at the top. A blank date usually means the profile has not been used for a very long time.

Step 2: List the profiles

Get-CimInstance -CimSession $s -ClassName Win32_UserProfile |
  Where-Object { -not $_.Special -and -not $_.Loaded } |
  Select-Object LocalPath, LastUseTime, SID

This leaves out system profiles and anyone currently signed in. Make a note of the exact path of the profile you want to remove. It may not be what you expect: if someone has had a profile on the PC before, Windows can name the folder something like jbloggs.DOMAIN or jbloggs.000.

Step 3: Remove the profile

Do a dry run first with -WhatIf:

Get-CimInstance -CimSession $s -ClassName Win32_UserProfile |
  Where-Object { $_.LocalPath -eq 'C:\Users\jbloggs' } |
  Remove-CimInstance -WhatIf

If the output shows the right profile, run the same command again without -WhatIf. Then run the listing from Step 2 to confirm it has gone, and close the session with Remove-CimSession $s to tidy things up.

Things to watch out for

  • Check for data first. Anything saved on the Desktop, in Documents or in Downloads is deleted with the profile, unless it is synced to OneDrive or redirected elsewhere.
  • The profile must not be in use. If the user is signed in, or something is running under their account, the delete will fail.
  • “The RPC server is unavailable” means the PC’s firewall is blocking remote WMI. In that case, run the commands locally on the PC through your remote management tool instead, or enable WinRM.
  • The user’s account is not affected. Only the local profile on that PC is removed. If they sign in again, Windows simply creates a fresh profile.

Keeping it tidy

If this keeps coming up, a Group Policy setting can do it automatically: Computer Configuration > Administrative Templates > System > User Profiles > Delete user profiles older than a specified number of days on system restart. Choose the number of days carefully, so you don’t remove profiles belonging to people who only use a PC occasionally.

If your PCs are running low on space or you would like help keeping them in good shape, get in touch with AGGIA IT.

Leave a Reply

Your email address will not be published. Required fields are marked *

Share the Post:

Related Posts