PowerShell - Удаление профилей с сервера

Записка от 29.11.2013

На терминальных серверах существует проблема с автоматическим удалением перемещаемых профилей (roaming profiles), если в терминальной сессии происходил сбой какой-нибудь программы - временные файлы программы блокируют удаление профиля с терминального сервера. При следующем входе в терминальную сессию для пользователя создастся новый профиль с каким-то суфиксом, так как старый неудаленный профиль не даст корректно переместить профиль с файлового сервера на терминальный. Со временем таких неудаленных профилей накопится очень много, что замусорит систему.

Для решения вышеописанной проблемы рекомендую использовать скрипт удаления профилей с терминального сервера, который должен быть запущен сразу же после перезагрузки сервера, когда терминальные сессии к серверу еще не установлены, то есть когда на сервере по определению не может быть профилей пользователей.

Внимание! Ни в коем случае нельзя удалять профиль пользователя (или его часть), если открыта сессия пользователя к терминальному серверу. В этом случае при завершении сессии пользователя на файловый сервер вернется неправильная версия профиля.
Внимание! На терминальных серверах без перемещаемых профилей данная проблема не возникает - применять скрипт удаления профилей не нужно. Для освобождения места на дисках нужно очищать временные файлы в каждом профиле, когда пользователь не подключен к серверу.

Ниже приведен код скрипта удаления профилей Remove-Profiles.ps1.

# Получение пути к скрипту
$ScriptFolder = $MyInvocation.MyCommand.Path.SubString(0,($MyInvocation.MyCommand.Path.Length - `
  $MyInvocation.MyCommand.Name.Length))
# Формирование пути к лог-файлу
$LogFile = $ScriptFolder + $MyInvocation.MyCommand.Name.SubString(0,($MyInvocation.MyCommand.Name.Length `
  - 4)) + ".log"
# Создание лог-файла
Out-File -FilePath $LogFile

# Указание папки профилей
$ProfileFolder = "C:\Users"
# Указание служебных папок
$ExcludedProfiles = "Default", "Default User", "Public", "All Users", "Administrator"

# Получение объектов из папки профилей
$SubDirs= Get-ChildItem $ProfileFolder -Force

# Обработка объектов из папки профилей
ForEach ($Dir in $SubDirs) {
    $LogMessage = $Nothing
    # Проверка, что объект существует и это папка
    If (Test-Path $Dir.FullName -PathType Container) {
        # Проверка, что профиль - это не служебная папка
        $NotDeleteFlag = $False
        ForEach ($ExcludedProfile in $ExcludedProfiles) {
            If ($Dir.Name -eq $ExcludedProfile) {
                $NotDeleteFlag = $True
            }
        }
        # Удаление профиля
        If ($NotDeleteFlag -eq $False) {
            # Формирование события для лога
            $LogMessage = get-date -uformat "%d.%m.%Y %H:%M:%S"
            $LogMessage += "`t"
            $LogMessage += $Dir.FullName + " is deleted."
            # Удаление папки
            #Remove-Item -Path $Dir.FullName -Force -Recurse
            $CmdLine = "cmd /c RD /S /Q """ + $Dir.FullName + """"
            Invoke-Expression -command $CmdLine
        }
        Else {
            # Формирование события для лога
            $LogMessage = get-date -uformat "%d.%m.%Y %H:%M:%S"
            $LogMessage += "`t"
            $LogMessage += $Dir.FullName + " is skipped as a service folder."
        }
    }
    Else {
        # Формирование события для лога
        $LogMessage = get-date -uformat "%d.%m.%Y %H:%M:%S"
        $LogMessage += "`t"
        $LogMessage += $Dir.FullName + " is skipped as a file."
    }
    # Запись события в лог-файл
    $LogMessage | Out-File -FilePath $LogFile -Append
}

Обращаю внимание, что в скрипте закомментирована строка удаления папок средствами PowerShell

Remove-Item -Path $Dir.FullName -Force -Recurse

Это сделано по причине того, что PowerShell не может удалить папки, на которые у пользователя нет прав, даже если оболочка запущена с повышенными административными правами. Проблема решается вызовом обычной командной строки с командой RD, которая не имеет указанной проблемы.

$CmdLine = "cmd /c RD /S /Q """ + $Dir.FullName + """"
Invoke-Expression -command $CmdLine

Перед использованием скрипта на сервере нужно разрешить выполнение неподписанных сценариев командой

Set-ExecutionPolicy -ExecutionPolicy "RemoteSigned"

Проверить текущую политику выполнения сценариев можно командой

Get-ExecutionPolicy

Чтобы корректно удалять профили, нужно чтобы все сессии этих профилей были завершены и перемещаемые профили вернулись на файловый сервер, если это возможно. Форсировать завершение сессий и отправку профилей на файловый сервер можно перезагрузкой сервера. В планировщике заданий Windows нужно поставить 2 задачи:
- задачу на перезагрузку сервера раз в неделю перед началом рабочего дня;
- задачу на выполнение скрипта от имени системы с повышенными административными правами при загрузке сервера.

Запуск скрипта лучше осуществлять через командный файл

powershell.exe "%~DP0Remove-Profiles.ps1"

После выполнения сценария все профили, которые зависли на терминальном сервере будут стерты. А копии профилей с правильной конфигурацией останутся на файловом сервере. То есть при входе в терминальную сессию пользователь получит последнюю правильную копию своего профиля.

Вверх