- Обзор
- Обозначения переменных
- Step 1: Обзор оптимизации папки WinSxS
- Step 2: Обзор очистки временных папок
- Step 3: Обзор ротации файлов журналов
- Step 4: Обзор обслуживания журналов событий
- Step 5: Подход к проектированию скриптов
- Step 6: Скрипт очистки Temp
- Step 7: Скрипт ротации логов
- Step 8: Скрипт оптимизации WinSxS
- Step 9: Скрипт обслуживания журналов событий
- Step 10: Примеры регистрации задач в Планировщике
- Итоги
Обзор
В этой статье периодические задачи по очистке диска в Windows Server разделяются на четыре категории и автоматизируются с помощью отдельных скриптов PowerShell для каждой задачи:
- Оптимизация WinSxS (сокращение component store)
- Очистка временных папок (по числу дней хранения)
- Ротация файлов журналов (несколько путей, каждый со своим сроком хранения)
- Резервное копирование и очистка журналов событий
Обозначения переменных
| Имя переменной | Пример | Описание |
|---|---|---|
<<ADMIN_USER>> |
Administrator |
Учетная запись, от имени которой выполняются задачи планировщика |
<<LOG_PATH>> |
C:\Maintenance\Logs |
Общая папка для логов выполнения скриптов |
<<BACKUP_PATH>> |
C:\Maintenance\Backups |
Папка для резервных копий журналов событий |
<<DAILY_TEMP_TASK_NAME>> |
DailyTempCleanup |
Имя задачи очистки Temp |
<<DAILY_LOG_TASK_NAME>> |
DailyLogRotation |
Имя задачи ротации логов |
<<MONTHLY_WINSXS_TASK_NAME>> |
MonthlyWinSxSCleanup |
Имя задачи оптимизации WinSxS |
<<MONTHLY_EVENTLOG_TASK_NAME>> |
MonthlyEventLogMaintenance |
Имя задачи обслуживания журналов событий |
Step 1: Обзор оптимизации папки WinSxS
C:\Windows\WinSxS — это хранилище компонентов (component store), в котором поддерживается история обновлений и компонент. Со временем оно разрастается и может быть оптимизировано следующими командами DISM:
# Анализ хранилища компонентов
Dism /Online /Cleanup-Image /AnalyzeComponentStore
# Очистка старых компонентов
Dism /Online /Cleanup-Image /StartComponentCleanup
# Полное удаление старых версий (откат невозможен)
Dism /Online /Cleanup-Image /StartComponentCleanup /ResetBase
Так как /ResetBase делает невозможной деинсталляцию старых обновлений, безопаснее выполнять эту операцию только по плану, например один раз в месяц в выделенное окно обслуживания.
Step 2: Обзор очистки временных папок
В $env:TEMP и C:\Windows\Temp накапливаются временные файлы, создаваемые установщиками и приложениями. Вместо того чтобы удалять всё подряд, на практике разумнее использовать политику:
«удалять файлы, которые старше X дней».
Step 3: Обзор ротации файлов журналов
Директории с логами растут вместе с числом приложений и при длительном хранении могут существенно заполнять диск. Поскольку желательно задавать индивидуальный срок хранения для каждой папки, удобнее управлять целями ротации через конфигурационный файл PSD1.
Step 4: Обзор обслуживания журналов событий
Журналы событий критичны для расследования инцидентов и аудита, поэтому их нельзя часто очищать без предварительного резервного копирования. Распространённый шаблон (примерно раз в месяц) — сначала экспортировать в .evtx, затем очистить журнал.
# Базовый пример резервного копирования и очистки
wevtutil epl System C:\Logs\System_20250101.evtx
wevtutil cl System
Step 5: Подход к проектированию скриптов
В статье используются четыре скрипта — по одному на каждую задачу:
-
cleanup_temp.ps1
Удаляет старые файлы во временных папках (количество дней хранения задаётся параметром). -
rotate_logs.ps1
Удаляет файлы.logв директориях, заданных в конфигурационном файле PSD1. -
optimize_winsxs.ps1
Выполняет/StartComponentCleanup /ResetBaseдля WinSxS и регистрирует объём освобождённого места. -
maintain_eventlogs.ps1
Экспортирует указанные журналы событий в.evtx, затем очищает эти журналы.
Такая структура позволяет запускать каждый скрипт отдельно из Планировщика заданий, комбинируя дневные и месячные задания и гибко меняя политику.
Пример структуры каталогов:
C:\
└─ Maintenance\
├─ cleanup_temp.ps1 # Очистка временных папок (ежедневно)
├─ rotate_logs.ps1 # Ротация логов (ежедневно)
├─ optimize_winsxs.ps1 # Оптимизация WinSxS (ежемесячно)
├─ maintain_eventlogs.ps1 # Резервное копирование + очистка журналов событий (ежемесячно)
│
├─ log_rotation.psd1 # Конфигурация ротации (несколько путей + дни хранения)
│
├─ Logs\ # Логи выполнения каждого скрипта
│ ├─ cleanup_temp_*.log
│ ├─ rotate_logs_*.log
│ ├─ optimize_winsxs_*.log
│ └─ eventlog_maint_*.log
│
└─ Backups\ # Резервные копии журналов событий (EVTX)
├─ System_YYYYMMDD.evtx
├─ Application_YYYYMMDD.evtx
└─ Security_YYYYMMDD.evtx
Step 6: Скрипт очистки Temp
Этот скрипт удаляет файлы и каталоги старше DaysToKeep дней в каждой папке из $TempPaths и записывает результат в лог-файл.
Параметры DaysToKeep и LogPath можно переопределить при запуске.
cleanup_temp.ps1
param(
[string[]]$TempPaths = @("$env:TEMP", "C:\Windows\Temp"),
[int]$DaysToKeep = 7,
[string]$LogPath = "<<LOG_PATH>>"
)
if (-not (Test-Path $LogPath)) {
New-Item $LogPath -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = Join-Path $LogPath "cleanup_temp_$timestamp.log"
function Write-Log($Message) {
"[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message |
Tee-Object -FilePath $logFile -Append
}
Write-Log "===== Temp cleanup started ====="
Write-Log "DaysToKeep = $DaysToKeep"
$limitDate = (Get-Date).AddDays(-$DaysToKeep)
foreach ($path in $TempPaths) {
if (-not (Test-Path $path)) {
Write-Log "Skip: Not found -> $path"
continue
}
Write-Log "Processing: $path"
Get-ChildItem -Path $path -Recurse -Force -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt $limitDate } |
ForEach-Object {
try {
Remove-Item -LiteralPath $_.FullName -Force -Recurse -ErrorAction Stop
Write-Log "Deleted: $($_.FullName)"
}
catch {
Write-Log "Failed: $($_.FullName) - $($_.Exception.Message)"
}
}
}
Write-Log "===== Temp cleanup finished ====="
Step 7: Скрипт ротации логов
Конфигурационный файл ротации (PSD1)
Этот файл PSD1 описывает несколько целевых директорий и соответствующее число дней хранения. Для добавления новой папки логов достаточно добавить ещё один элемент в список.
log_rotation.psd1
@{
RotationTargets = @(
@{
Path = "C:\Logs\App1"
DaysToKeep = 7
},
@{
Path = "C:\Logs\App2"
DaysToKeep = 30
},
@{
Path = "<<LOG_PATH>>"
DaysToKeep = 7
}
)
}
Скрипт ротации логов
Этот скрипт читает RotationTargets из PSD1 и удаляет файлы .log в каждой папке, если их LastWriteTime старше расчётного предела хранения. Все действия пишутся в лог.
rotate_logs.ps1
param(
[string]$ConfigPath = "C:\Maintenance\log_rotation.psd1",
[string]$LogPath = "<<LOG_PATH>>"
)
if (-not (Test-Path $LogPath)) {
New-Item $LogPath -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = Join-Path $LogPath "rotate_logs_$timestamp.log"
function Write-Log($Message) {
"[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message |
Tee-Object -FilePath $logFile -Append
}
Write-Log "===== Log rotation started ====="
Write-Log "ConfigPath = $ConfigPath"
if (-not (Test-Path $ConfigPath)) {
Write-Log "Config file not found. Exit."
exit 1
}
try {
$config = Import-PowerShellDataFile -Path $ConfigPath
}
catch {
Write-Log "Config load failed: $($_.Exception.Message)"
exit 1
}
foreach ($target in $config.RotationTargets) {
$targetPath = $target.Path
$daysToKeep = [int]$target.DaysToKeep
if (-not (Test-Path $targetPath)) {
Write-Log "Skip (not found): $targetPath"
continue
}
$limitDate = (Get-Date).AddDays(-$daysToKeep)
Write-Log "Path=$targetPath DaysToKeep=$daysToKeep Limit=$limitDate"
Get-ChildItem -Path $targetPath -Recurse -Include *.log -ErrorAction SilentlyContinue |
Where-Object { $_.LastWriteTime -lt $limitDate } |
ForEach-Object {
try {
Remove-Item -LiteralPath $_.FullName -Force -ErrorAction Stop
Write-Log "Deleted: $($_.FullName)"
}
catch {
Write-Log "Failed: $($_.FullName) - $($_.Exception.Message)"
}
}
}
Write-Log "===== Log rotation finished ====="
Step 8: Скрипт оптимизации WinSxS
Этот скрипт измеряет размер директории WinSxS до и после очистки и выполняет /StartComponentCleanup /ResetBase. Это позволяет количественно увидеть, сколько места было освобождено.
optimize_winsxs.ps1
param(
[string]$LogPath = "<<LOG_PATH>>"
)
if (-not (Test-Path $LogPath)) {
New-Item $LogPath -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = Join-Path $LogPath "optimize_winsxs_$timestamp.log"
function Write-Log($Message) {
"[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message |
Tee-Object -FilePath $logFile -Append
}
Write-Log "===== WinSxS optimization started ====="
$winsxsPath = "C:\Windows\WinSxS"
Write-Log "Measuring WinSxS size before..."
$sizeBefore = (Get-ChildItem $winsxsPath -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object Length -Sum).Sum
$sizeBeforeGB = [math]::Round($sizeBefore / 1GB, 2)
Write-Log "Before: $sizeBeforeGB GB"
Dism /Online /Cleanup-Image /AnalyzeComponentStore |
Out-File (Join-Path $LogPath "dism_before_$timestamp.txt")
Write-Log "Running StartComponentCleanup /ResetBase..."
Dism /Online /Cleanup-Image /StartComponentCleanup /ResetBase |
Out-File (Join-Path $LogPath "dism_exec_$timestamp.txt")
Write-Log "Measuring WinSxS size after..."
$sizeAfter = (Get-ChildItem $winsxsPath -Recurse -Force -ErrorAction SilentlyContinue |
Measure-Object Length -Sum).Sum
$sizeAfterGB = [math]::Round($sizeAfter / 1GB, 2)
Write-Log "After: $sizeAfterGB GB"
Write-Log ("Reduced: {0} GB" -f ([math]::Round(($sizeBefore - $sizeAfter)/1GB,2)))
Dism /Online /Cleanup-Image /AnalyzeComponentStore |
Out-File (Join-Path $LogPath "dism_after_$timestamp.txt")
Write-Log "===== WinSxS optimization finished ====="
Step 9: Скрипт обслуживания журналов событий
Этот скрипт экспортирует указанные журналы (по умолчанию: System / Application / Security) в файлы .evtx в каталог резервного копирования и затем очищает исходные журналы. Лог-файл детально фиксирует успешные и неуспешные операции.
maintain_eventlogs.ps1
param(
[string]$LogPath = "<<LOG_PATH>>", # Папка для логов (.log)
[string]$BackupPath = "<<BACKUP_PATH>>", # Папка для резервных копий EVTX
[string[]]$EventLogs = @("System", "Application", "Security")
)
# Создать директорию логов при необходимости
if (-not (Test-Path $LogPath)) {
New-Item $LogPath -ItemType Directory -Force | Out-Null
}
# Создать директорию резервного копирования при необходимости
if (-not (Test-Path $BackupPath)) {
New-Item $BackupPath -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format 'yyyyMMdd_HHmmss'
$logFile = Join-Path $LogPath "eventlog_maint_$timestamp.log"
function Write-Log($Message) {
"[{0}] {1}" -f (Get-Date -Format "yyyy-MM-dd HH:mm:ss"), $Message |
Tee-Object -FilePath $logFile -Append
}
Write-Log "===== Event log maintenance started ====="
Write-Log "BackupPath = $BackupPath"
foreach ($name in $EventLogs) {
# Уникальное имя EVTX для каждого журнала
$destEvtx = Join-Path $BackupPath ("{0}_{1}.evtx" -f $name, $timestamp)
try {
Write-Log "Export: $name -> $destEvtx"
wevtutil epl $name $destEvtx
Write-Log "Clear: $name"
wevtutil cl $name
}
catch {
Write-Log "Failed: $name - $($_.Exception.Message)"
}
}
Write-Log "===== Event log maintenance finished ====="
Step 10: Примеры регистрации задач в Планировщике
Ниже все скрипты обслуживания регистрируются с помощью schtasks.exe.
Подробное объяснение параметров /SC, /D, /ST, /RU и других см. в статье:
Управление Планировщиком заданий с помощью schtasks.exe
Пример ежедневной задачи очистки Temp
Задача cleanup_temp.ps1 выполняется каждый день в 2:00 и удаляет временные файлы старше 7 дней.
Задача запускается от имени <<ADMIN_USER>> с максимальными привилегиями (/RL HIGHEST).
schtasks /Create `
/TN "<<DAILY_TEMP_TASK_NAME>>" `
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Maintenance\cleanup_temp.ps1 -DaysToKeep 7 -LogPath <<LOG_PATH>>" `
/SC DAILY `
/ST 02:00 `
/RU "<<ADMIN_USER>>" `
/RL HIGHEST `
/F
Пример ежедневной задачи ротации логов
Задача rotate_logs.ps1 выполняется каждый день в 2:30 и ротирует файлы .log в нескольких директориях согласно конфигурации PSD1.
Сроки хранения управляются только через log_rotation.psd1.
schtasks /Create `
/TN "<<DAILY_LOG_TASK_NAME>>" `
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Maintenance\rotate_logs.ps1 -ConfigPath C:\Maintenance\log_rotation.psd1 -LogPath <<LOG_PATH>>" `
/SC DAILY `
/ST 02:30 `
/RU "<<ADMIN_USER>>" `
/RL HIGHEST `
/F
Пример ежемесячной задачи оптимизации WinSxS
Задача optimize_winsxs.ps1 выполняется 1-го числа каждого месяца в 3:00, выполняя StartComponentCleanup /ResetBase с измерением размера до и после.
Так как операция необратима, её необходимо планировать в окно обслуживания.
schtasks /Create `
/TN "<<MONTHLY_WINSXS_TASK_NAME>>" `
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Maintenance\optimize_winsxs.ps1 -LogPath <<LOG_PATH>>" `
/SC MONTHLY `
/D 1 `
/ST 03:00 `
/RU "<<ADMIN_USER>>" `
/RL HIGHEST `
/F
Пример ежемесячной задачи обслуживания журналов событий
Задача maintain_eventlogs.ps1 выполняется 1-го числа каждого месяца в 3:30, экспортируя журналы System / Application / Security в каталог резервного копирования и затем очищая их.
Отрегулируйте параметры -EventLogs и -BackupPath в соответствии с требованиями аудита.
schtasks /Create `
/TN "<<MONTHLY_EVENTLOG_TASK_NAME>>" `
/TR "powershell.exe -NoProfile -ExecutionPolicy Bypass -File C:\Maintenance\maintain_eventlogs.ps1 -LogPath <<LOG_PATH>>" `
/SC MONTHLY `
/D 1 `
/ST 03:30 `
/RU "<<ADMIN_USER>>" `
/RL HIGHEST `
/F
Итоги
В статье показано, как разделить обслуживание диска в Windows Server на отдельные скрипты по задачам и использовать конфигурационный файл PSD1 для гибкого управления целевыми директориями ротации логов и сроками их хранения.
- Очистка Temp: ежедневно, с настраиваемым числом дней хранения
- Ротация логов: несколько путей и сроки хранения, заданные в PSD1
- Оптимизация WinSxS: ежемесячно, с логированием объёма освобождённого места
- Обслуживание журналов событий: ежемесячно, с резервным копированием в EVTX и последующей очисткой
Такой подход даёт надёжную и расширяемую схему автоматизации обслуживания для производственных сред.
