Скрипт перемещения папок профилей RDS
Добрый день! Уважаемые читатели и гости одного из крупнейших IT порталов в рунете Pyatilistnik.org. В прошлый раз мы с вами успешно устранили и нашли причины ошибки whea uncorrectable error. Идем дальше по пути системного администрирования и сегодня я хочу с вами поделиться скриптом на PowerShell, который поможете перемещать неиспользуемые папки с перемещаемыми профилями RDS на файловую шару с архивными данными.
Описание задачи
Не так давно я производил обновление RDS фермы с Windows Server 2012 R2 до Windows Server 2016, там основной задачей было перенастройка папок перемещаемых профилей из-за изменения имени с v2 на v6. После переноса общее количество папок было свыше 6000, они занимали уже приличное дисковое пространство. Появилась идея удалить лишние по критериям:
- Учетная запись пользователя Active Directory отключена более 60 дней назад
- Перемещать папку по сети на архивный сервер
- Двигать за день не более 25 папок, чтобы в случае чего не было большого количества заявок
- Сохранить все права на папки и подпапки
- Разместить скрипт в запланированные задания и запускать по расписанию.
Содержимое скрипта
Напоминаю, что проверить скрипт и подредактировать его вы можете через PowerShell ISE, а запускать через саму оболочку PowerShell в режиме администратора.
#Requires -RunAsAdministrator
function Date {Get-Date -format "HH:mm:ss dd.MM.yyyy"}
# Указывается папка для создания логов и формат названия файлов, если ее нет, то создаем ее
$log_folder = "$PSScriptRoot\Logs\" + $($MyInvocation.MyCommand.Name -replace (".ps1", ""))
$log = "$log_folder\$(Get-Date -Format "yyyy_MM_dd_HH_mm_ss").txt"
if (! (Test-Path $log_folder -PathType Container -ErrorAction SilentlyContinue))
{
New-Item $log_folder -ItemType D -Force | Out-Null
}
"$(Date) Start Processing" | Tee-Object $log -Append
### Максимально мы перемещаем 25 папок
$max_folders_to_move = 25
# Определяем переменные, где указываются источники исходных папок и их конечное местоположение
$source_ts_profiles_folder = "\\Dfs07.root.pyatilistnik.org\m$\Share\tsProfiles_TermNew"
$dest_ts_profiles_folder = "\\SBU03.root.pyatilistnik.org\D$\share\Unused Files\tsProfiles_TermNew"
# Определяем переменные, где указываются источники исходных папок и их конечное местоположение
$source_home_folder1 = "\\Dfs05.root.pyatilistnik.org\S$\Share\home1"
$source_home_folder2 = "\\Dfs05.root.pyatilistnik.org\S$\Share\home2"
$dest_home_folder = "\\S03.root.pyatilistnik.org\D$\share\Unused Files\home"
# Задается поиск Active Directory, где указывается сервер глобального каталога
$DCs = @{}
foreach ($domain in (Get-ADForest).Domains)
{
$DC = ""
$DC = (Get-ADDomain $domain).ReplicaDirectoryServers | select -First 1
$DCs.Add($domain, $DC)
}
$GC = $DCs."root.pyatilistnik.org"+":3268"
"$(Date) Will use `"$GC`" as Global Catalogue Server" | Tee-Object $log -Append
### Объявляем первый раз переменную $i
$i = 1
foreach ($folder in (Get-ChildItem $source_ts_profiles_folder -Directory)) # | select -First 10
{
if ($i -gt $max_folders_to_move) {Continue}
$username = $domain = $domain_long = $user = $server = $lastLogonTimeStamp = $null
# Делим имя папки формата domain\username.v6
$domain = $($folder.Name -split "\.")[-2]
$v = $($folder.Name -split "\.")[-1]
$domain_long = $DCs.Keys | ? {$_ -like "$domain*"}
$username = $folder.Name -replace (".$domain.$v", "")
if (! $domain_long)
{
"$(Date) Unable to identify domain. Please check folder `"$($folder.Name)`"" | Tee-Object $log -Append
Continue
}
$server = $DCs.$domain_long
# "$(Date) Trying to find user `"$username`" in domain $domain/$domain_long using DC $server" | Tee-Object $log -Append
try {
$user = Get-ADUser $username -Properties lastLogonTimeStamp -Server $server -ErrorAction Stop
}
catch {
"$(Date) $($_.exception.message)" | Tee-Object $log -Append
}
$lastLogonTimeStamp_diff = (New-TimeSpan -Start $([datetime]::FromFileTime($user.lastLogonTimeStamp)) -End $(Get-Date)).Days
# Указывается, что ищем пользователей со статусом выключен минимум 60 дней
if ($user.Enabled -eq $false -and $lastLogonTimeStamp_diff -gt 60)
{
"$(Date) User `"$username`" Enabled status is $($user.Enabled), lastLogonTimeStamp diff days: $lastLogonTimeStamp_diff" | Tee-Object $log -Append
"$(Date) Going to move folder $i/$max_folders_to_move `"$($folder.Fullname)`" to `"$dest_ts_profiles_folder`"" | Tee-Object $log -Append
# Вызов утилиты утилиты robocopy с нужными ключами
$log_robocopy = "$log_folder\robocopy_" + (Get-Date -Format "yyyy_MM_dd_HH_mm_ss") + ".log"
& robocopy `"$($folder.Fullname)`" `"$dest_ts_profiles_folder\$($folder.Name)`" /NJH /MOVE /S /B /UNILOG:$log_robocopy # /L
$i++
}
}
###
$i = 1
foreach ($folder in (Get-ChildItem $source_home_folder1, $source_home_folder2 -Directory)) # | select -First 10
{
if ($i -gt $max_folders_to_move) {Continue}
$username = $user = $users_found = $lastLogonTimeStamp = $null
$username = $folder.Name # -replace (".$domain.$v", "")
# "$(Date) Trying to find user `"$username`" in thw whole AD forest" | Tee-Object $log -Append
try {
$user = Get-ADUser $username -Properties lastLogonTimeStamp -Server $GC -ErrorAction Stop
}
catch {
"$(Date) $($_.exception.message)" | Tee-Object $log -Append
}
$users_found = ($user | Measure-Object).Count
# "$(Date) Found users: $users_found" | Tee-Object $log -Append
if ($users_found -ne 1)
{
"$(Date) Need to check AD for folder `"$($folder.FullName)`'" | Tee-Object $log -Append
Continue
}
$lastLogonTimeStamp_diff = (New-TimeSpan -Start $([datetime]::FromFileTime($user.lastLogonTimeStamp)) -End $(Get-Date)).Days
# Указывается, что ищем пользователей со статусом выключен минимум 60 дней
if ($user.Enabled -eq $false -and $lastLogonTimeStamp_diff -gt 60)
{
"$(Date) User `"$username`" Enabled status is $($user.Enabled), lastLogonTimeStamp diff days: $lastLogonTimeStamp_diff" | Tee-Object $log -Append
"$(Date) Going to move folder $i/$max_folders_to_move `"$($folder.Fullname)`" to `"$dest_home_folder`"" | Tee-Object $log -Append
# Вызов утилиты утилиты robocopy с нужными ключами
$log_robocopy = "$log_folder\robocopy_" + (Get-Date -Format "yyyy_MM_dd_HH_mm_ss") + ".log"
& robocopy `"$($folder.Fullname)`" `"$dest_home_folder\$($folder.Name)`" /NJH /MOVE /S /B /UNILOG:$log_robocopy # /L
$i++
}
}
###
"$(Date) End Processing" | Tee-Object $log -Append
Скачать готовый скрипт по перемещению папок
Напоминаю, что скрипт будет иметь формат архива, который вам нужно будет распаковать, и для запуска скриптов вам необходимо будет изменить политику запуска неподписанных сценариев.
Теперь приведу некоторые настройки, которые я использую при запуске скрипта в планировщике Windows. Во первых у вас должна быть учетная запись, которая будет использоваться для запуска и у нее должны быть права на чтение пользователей из Active Directory, далее запускаем не зависимо залогинен кто-то или нет и с максимальными правами.
На вкладке действия укажите, что в качестве программы запуска будет использоваться Powershell и в аргументах укажите полный путь до вашего скрипта.
На этом у меня все. Вы смело можете дописать или модифицировать данный скрипт под свои нужды. С вами был Иван Семин, автор и создатель IT портала Pyatilistnik.org.