Администраторы должны время от времени удалять старые профили пользователей (вышедших на пенсию, неактивных пользователей и т. д.) из папки C:\Users на рабочих станциях и серверах Windows. Задача очистки профилей пользователей в Windows чаще всего выполняется на терминальных серверах Remote Desktop Services (RDS).

Основной проблемой RDS-серверов является постоянный рост размера каталогов пользовательских профилей на жестком диске. Эта проблема частично решается с помощью квот на размер пользовательских профилей, использующих FSRM или NTFS-квоты, перемещаемых профилей, таких как FSLogix или User Profile Disk, перенаправляемых папок и т. д. Однако если у вас большое количество пользователей RDS, со временем папка C:\Users будет содержать большое количество каталогов со старыми (неиспользуемыми) профилями пользователей.

Как удалить профиль пользователя в Windows вручную?

В Windows профиль можно удалить вручную из панели управления:

  1. Откройте Дополнительные настройки системы (выполните команду SystemPropertiesAdvanced ) и перейдите к Профили пользователей -> Настройки;
  2. В этом окне перечислены все профили пользователей (локальные, доменные и учетные записи Microsoft), хранящиеся на этом компьютере. Размер каждого профиля пользователя на диске указан в поле Размер рубрика;
  3. Выберите пользователя, профиль которого вы хотите удалить, и нажмите кнопку Удалить кнопка. удаление профиля пользователя вручную в windowsудаление профиля пользователя вручную в windows

В Windows 11/10 и Windows Server 2022/2019 профили пользователей можно удалить с диска с помощью команды Настройки приложение. Перейдите к Счета -> Доступ к работе и школе (или запустите ярлык URI ms-settings:otherusers ). Выберите пользователя и нажмите кнопку Удалить чтобы удалить данные своего профиля с компьютера.

ms-settings - удаление профиля пользователя в windows 11ms-settings - удаление профиля пользователя в windows 11

При правильном удалении профиля пользователя в Windows удаляется каталог профиля в C:\Users и запись пользователя в реестре.

Многие начинающие администраторы пытаются вручную удалить каталог профиля пользователя из папки C:\Users. В этом случае необходимо вручную удалить ссылку на профиль из реестра Windows:

  1. Запустите редактор реестра (regedit.exe);
  2. Перейдите к ключу реестра HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList;
  3. Для каждого пользователя, вошедшего в систему локально (этот способ входа должен быть разрешен для пользователя параметром Разрешить вход в систему локально Опция GPO), создается отдельный подключ с SID пользователя в качестве имени;
  4. Вы можете найти ключ реестра, соответствующий пользователю, по его SID, или вручную просмотреть содержимое всех подключей, пока не найдете ключ, в котором ProfileImagePath значение указывает на каталог с профилем пользователя на диске (например, C:\Users\j.smith);profileimagepath в реестреprofileimagepath в реестре
  5. Удалите этот ключ реестра, чтобы завершить корректное удаление профиля.

Можно также удалить профиль конкретного пользователя с помощью PowerShell:

Get-CimInstance -Class Win32_UserProfile | Where-Object { $_.LocalPath.split(‘\’)[-1] -eq 'j.smith' } | Remove-CimInstance

Эта команда удаляет как каталог жесткого диска, так и j.smith ссылку на профиль пользователя в реестре HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList.

Эта команда работает как в Windows PowerShell, так и в новых версиях PowerShell Core 7.x.

Вы можете удалить профиль пользователя на удаленном компьютере с помощью PowerShell Remoting и команды Invoke-Command:

$compname="mun-wks92s3"
$user = "j.smith"
Invoke-Command -ComputerName $compname -ScriptBlock {
param($user)
Get-CimInstance -Class Win32_UserProfile | Where-Object { $_.LocalPath.split(‘\’)[-1] -eq $user } | Remove-CimInstance
} -ArgumentList $user

GPO: Удаление пользовательских профилей старше указанного количества дней

В Windows есть встроенная опция групповой политики для автоматического удаления пользовательских профилей старше xx дней. Включить эту опцию можно с помощью редактора локальной групповой политики (gpedit.msc) или с помощью консоли управления GPO домена (gpmc.msc). В этом примере мы применим политику автоматической очистки профиля к узлам в ферме RDS, которые находятся в отдельном контейнере (Organizational Unit, OU) в Active Directory.

Прежде чем применять политику очистки профилей ко всем хостам, мы настоятельно рекомендуем протестировать ее на тестовом хосте. Переведите один из серверов RDSH в режим обслуживания (drain) и протестируйте политику на нем.
  1. Найдите OU, содержащую компьютеры/серверы, к которым вы хотите применить политику очистки пользовательских профилей. Щелкните правой кнопкой мыши на OU и выберите Создать GPO в этом домене и связать его здесь;создать новый домен gpoсоздать новый домен gpo
  2. Укажите имя политики и отредактируйте GPO;
  3. Перейдите в Конфигурация компьютера -> Административные шаблоны -> Система -> Профили пользователей;
  4. Открыть опцию Удаление профилей пользователей старше указанного количества дней при перезагрузке системы;
  5. Включите политику и укажите количество дней, в течение которых профиль пользователя считается активным. По истечении этого срока служба Windows User Profile Service автоматически удалит профиль при следующем перезапуске. Рекомендуется указать 45-90 дни здесь;Групповая политика: Удаление профилей пользователей старше указанного количества дней при перезагрузке системыГрупповая политика: Удаление профилей пользователей старше указанного количества дней при перезагрузке системы
  6. После применения новых параметров групповой политики служба профилей пользователей на сервере Windows Server будет автоматически удалять старые профили пользователей. Профили пользователей будут удалены при следующей перезагрузке сервера.
При использовании этой политики необходимо убедиться в отсутствии проблем с системным временем при остановке/перезагрузке сервера (см. статью «Изменение системного времени и даты после перезагрузки»). В противном случае активные профили пользователей могут быть удалены.Еще одним недостатком является то, что вы не можете предотвратить удаление определенных профилей, таких как локальные учетные записи, администраторы и т. д.

Эта политика некорректно работала в версиях до Windows 11/10 и Windows Server 2022/2019. Ранее неактивность профиля пользователя определялась по дате изменения файла NTUSER.dat. При установке обновлений Windows служба Trusted Installer может изменить дату модификации файла NTUSER.dat файл в профиле каждого пользователя. В результате служба Win32_UserProfile считает, что профиль недавно использовался.

В современных версиях Windows этот параметр групповой политики проверяет активность пользовательского профиля по значениям параметра LocalProfileUnloadTimeLow и LocalProfileUnloadTimeHigh параметры в HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\<USER_SID>

Получение времени загрузки профиля из параметра реестра LocalProfileUnloadTimeHigh Получить время загрузки профиля из параметра реестра LocalProfileUnloadTimeHigh

Вы можете использовать следующий сценарий, чтобы получить LocalProfileLoadTimeLow и LocalProfileUnloadTimeHigh значения реестра в формате обычного времени:

$profilelist = Get-ChildItem "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList"
foreach ($p in $profilelist) {
    try {
        $objUser = (New-Object System.Security.Principal.SecurityIdentifier($p.PSChildName)).Translate([System.Security.Principal.NTAccount]).value
    } catch {
        $objUser = "[UNKNOWN]"
  }
    Remove-Variable -Force LTH,LTL,UTH,UTL -ErrorAction SilentlyContinue
    $LTH = '{0:X8}' -f (Get-ItemProperty -Path $p.PSPath -Name LocalProfileLoadTimeHigh -ErrorAction SilentlyContinue).LocalProfileLoadTimeHigh
    $LTL = '{0:X8}' -f (Get-ItemProperty -Path $p.PSPath -Name LocalProfileLoadTimeLow -ErrorAction SilentlyContinue).LocalProfileLoadTimeLow
    $UTH = '{0:X8}' -f (Get-ItemProperty -Path $p.PSPath -Name LocalProfileUnloadTimeHigh -ErrorAction SilentlyContinue).LocalProfileUnloadTimeHigh
    $UTL = '{0:X8}' -f (Get-ItemProperty -Path $p.PSPath -Name LocalProfileUnloadTimeLow -ErrorAction SilentlyContinue).LocalProfileUnloadTimeLow
    $LoadTime = if ($LTH -and $LTL) {
        [datetime]::FromFileTime("0x$LTH$LTL")
    } else {
        $null
    }
    $UnloadTime = if ($UTH -and $UTL) {
        [datetime]::FromFileTime("0x$UTH$UTL")
    } else {
        $null
    }
    [pscustomobject][ordered]@{
        User = $objUser
        SID = $p.PSChildName
        Loadtime = $LoadTime
        UnloadTime = $UnloadTime
    }
} 

Получение даты последней загрузки и выгрузки профиля с помощью PowerShellПолучение даты загрузки и выгрузки последнего профиля с помощью PowerShell

Этот список содержит время последней загрузки для каждого пользовательского профиля.

Удаление старых профилей пользователей с помощью сценария PowerShell

Вместо использования описанной выше политики автоматической очистки профилей можно использовать простой сценарий PowerShell для поиска и удаления профилей отключенных или неактивных пользователей.

Сначала попробуем рассчитать размер профиля каждого пользователя в C:\Users с помощью простого сценария из статьи Получение размера папки с помощью PowerShell

gci -force ‘C:\Users\’-ErrorAction SilentlyContinue | Where { !($_.Attributes -match " ReparsePoint") }| ? { $_ -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)

Общий размер всех профилей пользователей в C:\Users составляет около 32 ГБ.

подсчитать общий размер профилей пользователей на хосте RDSподсчитать общий размер профиля пользователя на хосте RDS

Сценарий игнорирует символические ссылки Windows (symlinks) при подсчете размера пользовательского профиля.

Посмотрим список пользователей, чьи профили не использовались более 60 дней. Вы можете использовать значение в поле LastUseTime поле профиля, чтобы найти их.

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

Оказалось, что на моем хосте RDS было 127 неактивных учетных записей пользователей (с профилями общим размером около 18 ГБ).

получить список неактивных пользователей по профилю LastUseTime на RDSHполучить список неактивных пользователей по профилю LastUseTime на RDSH

Следующий сценарий PowerShell выводит список сведений о профилях пользователей, которые не обновлялись более 60 дней. Сценарий преобразует SID пользователя в имя, вычисляет размер профиля каждого пользователя и выводит итоговую таблицу:

$allprofilesinfo = @()
$OldProfiles=Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and ($_.ConvertToDateTime($_.LastUseTime) -lt (Get-Date).AddDays(-60))}
Foreach ($OldProfile in $OldProfiles)
   {$objSID = New-Object System.Security.Principal.SecurityIdentifier ($OldProfile.SID)
    $objUser = $objSID.Translate( [System.Security.Principal.NTAccount])
        $userinfo = New-Object PSObject -Property @{
            userName = $objUser.Value
            ProfilePath = $OldProfile.localpath
            LastUsedDate = $OldProfile.ConvertToDateTime($OldProfile.LastUseTime)
            FolderSize =  "{0:N2} GB" -f ((gci –force $OldProfile.localpath –Recurse -ErrorAction SilentlyContinue| measure Length -s).sum / 1Gb) 
        }
    $allprofilesinfo += $userinfo
   }
$allprofilesinfo

powershell: list local profiles infopowershell: list local profiles info

Чтобы удалить все эти профили пользователей, достаточно передать список пользователей в файл Remove-WmiObject (рекомендуется проверить вывод скрипта с помощью команды -WhatIf параметр перед запуском):

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

Как упоминалось ранее, при установке некоторых обновлений Windows служба Trusted Installer может изменить дату модификации файла NTUSER.dat в профиле каждого пользователя.На скриншоте выше видно, что все профили были изменены примерно в одно и то же время. Проверьте дату последних установленных в Windows обновлений:

gwmi win32_quickfixengineering |sort installedon  |select InstalledOn -Last 1

Или с помощью модуля PSWindowsUpdate:

Get-WUHistory | Select-Object -First 10

Скорее всего, это совпадет с датой изменения профилей. Поэтому на более ранних версиях Windows список неактивных профилей можно получить с помощью другого сценария, который проверяет значение lastwritetime атрибут каталога профиля пользователя:

$USERS= (Get-ChildItem -directory -force 'C:\Users' | Where { ((Get-Date) — $_.lastwritetime).days -ge 60 } | % {'c:\users\' + $_.Name})
foreach ($User in $USERS) {
Get-WMIObject -class Win32_UserProfile | Where {(!$_.Special) -and (!$_.Loaded) -and ($_.LocalPath -eq $User)} | Remove-WmiObject WhatIf }

Чтобы избежать удаления профилей некоторых пользователей (например Система и Сетевая служба учетные записи, a локальный администратор учетную запись, учетные записи пользователей, имеющих активные сеансы, и другие учетные записи из исключение список), вы можете модифицировать скрипт следующим образом:

#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
}
}

Вы можете запустить этот сценарий PowerShell через GPO при выключении или с помощью сценария PowerShell в планировщике задач.

Перед настройкой автоматического удаления профиля рекомендуется протестировать сценарий в своей среде!

Можно изменить сценарий для автоматического удаления всех профилей пользователей, добавленных в определенную группу AD. Например, вы хотите удалить профили пользователей, которые уволились. Просто добавьте эти учетные записи в группу DisabledUsers группу и запустите сценарий на целевом хосте:

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