Найти в Дзене
ИТ АУТСОРСИНГ в СПб

5 PowerShell-скриптов для автоматизации рутинных задач администратора Windows Server

Привет, друзья! Сегодня я хочу поделиться с вами пятью PowerShell-скриптами, которые существенно облегчили мою жизнь как администратора Windows Server. За более, чем 10 лет работы в IT я перепробовал множество подходов к автоматизации, и эти скрипты оказались наиболее полезными в повседневной работе. Помню, как раньше тратил часы на выполнение однотипных задач вручную — проверял свободное место на дисках, искал проблемные службы, создавал отчеты о безопасности... Но с тех пор, как я освоил PowerShell и создал набор полезных скриптов, моя эффективность выросла в разы. Теперь я могу сосредоточиться на более важных задачах, требующих творческого подхода, а рутину доверить автоматизации. Давайте рассмотрим пять скриптов, которые я использую практически каждый день. Я постарался сделать их максимально универсальными, чтобы вы могли легко адаптировать их под свои нужды. Одна из самых распространенных проблем, с которой сталкиваются администраторы — это неожиданно заполнившиеся диски. Обычно
Оглавление

Привет, друзья! Сегодня я хочу поделиться с вами пятью PowerShell-скриптами, которые существенно облегчили мою жизнь как администратора Windows Server. За более, чем 10 лет работы в IT я перепробовал множество подходов к автоматизации, и эти скрипты оказались наиболее полезными в повседневной работе.

Помню, как раньше тратил часы на выполнение однотипных задач вручную — проверял свободное место на дисках, искал проблемные службы, создавал отчеты о безопасности... Но с тех пор, как я освоил PowerShell и создал набор полезных скриптов, моя эффективность выросла в разы. Теперь я могу сосредоточиться на более важных задачах, требующих творческого подхода, а рутину доверить автоматизации.

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

Скрипт №1: Мониторинг свободного места на дисках с оповещением

Одна из самых распространенных проблем, с которой сталкиваются администраторы — это неожиданно заполнившиеся диски. Обычно вы узнаете об этом, когда пользователи уже не могут сохранить файлы или приложения начинают сбоить. Чтобы предотвратить такие ситуации, я создал скрипт, который проверяет свободное место на дисках серверов и отправляет предупреждение, если оно падает ниже заданного порога.

# Скрипт для мониторинга свободного места на дисках
# DiskSpaceMonitor.ps1

# Настройки
$servers = "Server1", "Server2", "Server3" # Список серверов для проверки
$threshold = 15 # Порог свободного места в процентах
$logFile = "C:\Logs\DiskSpace_$(Get-Date -Format 'yyyyMMdd').log" # Путь к файлу журнала
$sendEmail = $true # Отправлять ли email-уведомления
$emailParams = @{
From = "monitoring@yourdomain.com"
To = "admin@yourdomain.com"
Subject = "Предупреждение: Низкий уровень свободного места на диске"
SmtpServer = "smtp.yourdomain.com"
}

# Создаем директорию для логов, если она не существует
if (!(Test-Path (Split-Path $logFile -Parent))) {
New-Item -ItemType Directory -Path (Split-Path $logFile -Parent) -Force | Out-Null
}

# Функция для записи в лог
function Write-Log {
param (
[string]$message
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$timestamp - $message" | Out-File -FilePath $logFile -Append
Write-Host "$timestamp - $message"
}

Write-Log "Начало проверки свободного места на дисках"

$lowSpaceFound = $false
$emailBody = "Обнаружены диски с низким уровнем свободного места:`n`n"

foreach ($server in $servers) {
Write-Log "Проверка сервера $server"

try {
$disks = Get-WmiObject -ComputerName $server -Class Win32_LogicalDisk -Filter "DriveType = 3" -ErrorAction Stop

foreach ($disk in $disks) {
$freeSpacePercent = [math]::Round(($disk.FreeSpace / $disk.Size) * 100, 2)

if ($freeSpacePercent -lt $threshold) {
$lowSpaceFound = $true
$freeSpaceGB = [math]::Round($disk.FreeSpace / 1GB, 2)
$totalSpaceGB = [math]::Round($disk.Size / 1GB, 2)
$message = "ПРЕДУПРЕЖДЕНИЕ: Сервер $server, диск $($disk.DeviceID), свободно: $freeSpacePercent% ($freeSpaceGB ГБ из $totalSpaceGB ГБ)"
Write-Log $message
$emailBody += "$message`n"
}
else {
$freeSpaceGB = [math]::Round($disk.FreeSpace / 1GB, 2)
$totalSpaceGB = [math]::Round($disk.Size / 1GB, 2)
Write-Log "OK: Сервер $server, диск $($disk.DeviceID), свободно: $freeSpacePercent% ($freeSpaceGB ГБ из $totalSpaceGB ГБ)"
}
}
}
catch {
Write-Log "ОШИБКА: Не удалось подключиться к серверу $server. Ошибка: $_"
}
}

# Отправляем email, если найдены диски с низким уровнем свободного места
if ($lowSpaceFound -and $sendEmail) {
try {
Send-MailMessage @emailParams -Body $emailBody
Write-Log "Email-уведомление отправлено"
}
catch {
Write-Log "ОШИБКА: Не удалось отправить email-уведомление. Ошибка: $_"
}
}

Write-Log "Проверка завершена"

Как использовать этот скрипт

  1. Сохраните скрипт в файл DiskSpaceMonitor.ps1
  2. Настройте параметры в начале скрипта (список серверов, порог свободного места, настройки email)
  3. Создайте задание в планировщике задач Windows для регулярного запуска скрипта

Я обычно настраиваю запуск этого скрипта каждое утро в 7:00, чтобы к началу рабочего дня уже знать, если где-то возникли проблемы с дисковым пространством. Также можно настроить запуск каждые несколько часов для более оперативного реагирования.

Преимущества этого скрипта

  • Проактивный мониторинг помогает предотвратить проблемы до того, как они повлияют на пользователей
  • Централизованный мониторинг множества серверов из одного места
  • Подробное логирование для отслеживания тенденций использования дискового пространства
  • Гибкая настройка порогов и способов оповещения

Скрипт №2: Автоматическое создание отчета о состоянии служб на серверах

Другая распространенная задача — проверка состояния критически важных служб на серверах. Вместо того, чтобы вручную подключаться к каждому серверу и проверять службы, я создал скрипт, который автоматически генерирует отчет о состоянии заданных служб на всех серверах.

# Скрипт для проверки состояния служб на серверах
# ServiceStatusReport.ps1

# Настройки
$servers = "Server1", "Server2", "Server3" # Список серверов для проверки
$services = @{
"Server1" = "MSSQLSERVER", "SQLServerAgent", "W3SVC", "BITS"
"Server2" = "MSSQLSERVER", "SQLServerAgent", "W3SVC", "BITS"
"Server3" = "W3SVC", "BITS", "WinRM"
}
$reportPath = "C:\Reports\ServiceStatus_$(Get-Date -Format 'yyyyMMdd').html" # Путь к файлу отчета
$sendEmail = $true # Отправлять ли email-отчет
$emailParams = @{
From = "monitoring@yourdomain.com"
To = "admin@yourdomain.com"
Subject = "Отчет о состоянии служб на серверах - $(Get-Date -Format 'yyyy-MM-dd')"
SmtpServer = "smtp.yourdomain.com"
}

# Создаем директорию для отчетов, если она не существует
if (!(Test-Path (Split-Path $reportPath -Parent))) {
New-Item -ItemType Directory -Path (Split-Path $reportPath -Parent) -Force | Out-Null
}

# Создаем HTML-заголовок отчета
$htmlHeader = @"
<!DOCTYPE html>
<html>
<head>
<title>Отчет о состоянии служб на серверах</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
h1 { color: #0066cc; }
table { border-collapse: collapse; width: 100%; margin-top: 20px; }
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
th { background-color: #f2f2f2; }
tr:nth-child(even) { background-color: #f9f9f9; }
.running { color: green; font-weight: bold; }
.stopped { color: red; font-weight: bold; }
.other { color: orange; font-weight: bold; }
.summary { margin-top: 20px; font-weight: bold; }
</style>
</head>
<body>
<h1>Отчет о состоянии служб на серверах</h1>
<p>Дата создания: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')</p>
"@

# Создаем HTML-содержимое отчета
$htmlBody = ""
$totalServices = 0
$runningServices = 0
$stoppedServices = 0
$otherServices = 0

foreach ($server in $servers) {
$htmlBody += "<h2>Сервер: $server</h2>"
$htmlBody += "<table><tr><th>Служба</th><th>Отображаемое имя</th><th>Состояние</th><th>Тип запуска</th></tr>"

try {
foreach ($service in $services[$server]) {
$totalServices++
$svc = Get-Service -ComputerName $server -Name $service -ErrorAction SilentlyContinue

if ($svc) {
$startupType = (Get-WmiObject -ComputerName $server -Class Win32_Service -Filter "Name='$service'").StartMode

$statusClass = "other"
if ($svc.Status -eq "Running") {
$statusClass = "running"
$runningServices++
}
elseif ($svc.Status -eq "Stopped") {
$statusClass = "stopped"
$stoppedServices++
}
else {
$otherServices++
}

$htmlBody += "<tr><td>$($svc.Name)</td><td>$($svc.DisplayName)</td><td class='$statusClass'>$($svc.Status)</td><td>$startupType</td></tr>"
}
else {
$htmlBody += "<tr><td>$service</td><td>Не найдена</td><td class='stopped'>Ошибка</td><td>Неизвестно</td></tr>"
$stoppedServices++
}
}
}
catch {
$htmlBody += "<tr><td colspan='4'>Ошибка при подключении к серверу: $_</td></tr>"
}

$htmlBody += "</table>"
}

# Добавляем сводную информацию
$htmlSummary = @"
<div class="summary">
<p>Всего проверено служб: $totalServices</p>
<p>Запущено: <span class="running">$runningServices</span></p>
<p>Остановлено: <span class="stopped">$stoppedServices</span></p>
<p>В другом состоянии: <span class="other">$otherServices</span></p>
</div>
"@

# Создаем HTML-футер отчета
$htmlFooter = @"
</body>
</html>
"@

# Собираем полный HTML-отчет
$htmlReport = $htmlHeader + $htmlBody + $htmlSummary + $htmlFooter

# Сохраняем отчет в файл
$htmlReport | Out-File -FilePath $reportPath -Encoding UTF8

# Отправляем email с отчетом
if ($sendEmail) {
try {
Send-MailMessage @emailParams -Body "Отчет о состоянии служб на серверах во вложении." -Attachments $reportPath -BodyAsHtml
Write-Host "Email с отчетом отправлен"
}
catch {
Write-Host "ОШИБКА: Не удалось отправить email с отчетом. Ошибка: $_"
}
}

Write-Host "Отчет создан: $reportPath"

Как использовать этот скрипт

  1. Сохраните скрипт в файл ServiceStatusReport.ps1
  2. Настройте параметры в начале скрипта (список серверов, список служб для каждого сервера, настройки отчета и email)
  3. Создайте задание в планировщике задач Windows для регулярного запуска скрипта

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

Преимущества этого скрипта

  • Автоматическое создание наглядного HTML-отчета
  • Возможность настройки списка проверяемых служб для каждого сервера
  • Цветовое выделение различных состояний служб для быстрого визуального анализа
  • Сводная статистика по всем проверенным службам

Скрипт №3: Управление учетными записями пользователей Active Directory (продолжение)

Управление учетными записями пользователей в Active Directory — еще одна задача, которая часто отнимает много времени. Особенно когда нужно создать несколько учетных записей с похожими параметрами или вы регулярно выполняете однотипные операции с учетными записями. Я создал универсальный скрипт, который помогает автоматизировать основные операции с пользователями AD.

# Скрипт для управления учетными записями пользователей Active Directory
# ADUserManager.ps1

# Импортируем модуль Active Directory
Import-Module ActiveDirectory

# Функция для создания нового пользователя
function New-ADUserFromTemplate {
param (
[Parameter(Mandatory=$true)]
[string]$FirstName,

[Parameter(Mandatory=$true)]
[string]$LastName,

[Parameter(Mandatory=$true)]
[string]$Department,

[Parameter(Mandatory=$true)]
[string]$Title,

[Parameter(Mandatory=$true)]
[string]$Manager,

[Parameter(Mandatory=$false)]
[string]$TemplateUser = "",

[Parameter(Mandatory=$false)]
[string]$OU = "OU=Users,DC=yourdomain,DC=com"
)

# Генерируем имя пользователя (первая буква имени + фамилия)
$username = ($FirstName.Substring(0, 1) + $LastName).ToLower()
$originalUsername = $username
$counter = 1

# Проверяем, существует ли пользователь с таким именем
while (Get-ADUser -Filter "SamAccountName -eq '$username'" -ErrorAction SilentlyContinue) {
$username = $originalUsername + $counter
$counter++
}

# Генерируем случайный пароль
$password = [System.Web.Security.Membership]::GeneratePassword(12, 3)
$securePassword = ConvertTo-SecureString -String $password -AsPlainText -Force

# Создаем базовые параметры для нового пользователя
$userParams = @{
SamAccountName = $username
UserPrincipalName = "$username@yourdomain.com"
Name = "$FirstName $LastName"
GivenName = $FirstName
Surname = $LastName
DisplayName = "$LastName, $FirstName"
Department = $Department
Title = $Title
Manager = (Get-ADUser -Filter "Name -eq '$Manager'" -ErrorAction SilentlyContinue).DistinguishedName
Enabled = $true
ChangePasswordAtLogon = $true
AccountPassword = $securePassword
Path = $OU
}

# Если указан шаблонный пользователь, копируем группы и другие атрибуты
if ($TemplateUser -and (Get-ADUser -Filter "SamAccountName -eq '$TemplateUser'" -ErrorAction SilentlyContinue)) {
try {
# Создаем пользователя
New-ADUser @userParams

# Получаем группы шаблонного пользователя
$templateGroups = Get-ADUser -Identity $TemplateUser -Properties MemberOf | Select-Object -ExpandProperty MemberOf

# Добавляем нового пользователя в те же группы
foreach ($group in $templateGroups) {
Add-ADGroupMember -Identity $group -Members $username
}

# Копируем дополнительные атрибуты (например, адрес, телефон и т.д.)
$templateUser = Get-ADUser -Identity $TemplateUser -Properties *
$newUser = Get-ADUser -Identity $username -Properties *

$attributesToCopy = @("StreetAddress", "City", "State", "PostalCode", "Country", "Company", "Office", "OfficePhone")

foreach ($attribute in $attributesToCopy) {
if ($templateUser.$attribute) {
Set-ADUser -Identity $username -Replace @{$attribute = $templateUser.$attribute}
}
}

Write-Host "Пользователь $username создан на основе шаблона $TemplateUser" -ForegroundColor Green
}
catch {
Write-Host "Ошибка при создании пользователя: $_" -ForegroundColor Red
}
}
else {
try {
# Создаем пользователя без шаблона
New-ADUser @userParams

# Добавляем пользователя в группу "Domain Users" (если еще не добавлен)
Add-ADGroupMember -Identity "Domain Users" -Members $username -ErrorAction SilentlyContinue

Write-Host "Пользователь $username создан" -ForegroundColor Green
}
catch {
Write-Host "Ошибка при создании пользователя: $_" -ForegroundColor Red
}
}

# Возвращаем информацию о созданном пользователе
return @{
Username = $username
Password = $password
Email = "$username@yourdomain.com"
}
}

# Функция для блокировки/разблокировки пользователя
function Set-ADUserStatus {
param (
[Parameter(Mandatory=$true)]
[string]$Username,

[Parameter(Mandatory=$true)]
[bool]$Enabled
)

try {
Set-ADUser -Identity $Username -Enabled $Enabled
$status = if ($Enabled) { "разблокирован" } else { "заблокирован" }
Write-Host "Пользователь $Username $status" -ForegroundColor Green
}
catch {
Write-Host "Ошибка при изменении статуса пользователя: $_" -ForegroundColor Red
}
}

# Функция для перемещения пользователя в другое подразделение
function Move-ADUserToOU {
param (
[Parameter(Mandatory=$true)]
[string]$Username,

[Parameter(Mandatory=$true)]
[string]$TargetOU
)

try {
$user = Get-ADUser -Identity $Username
Move-ADObject -Identity $user.DistinguishedName -TargetPath $TargetOU
Write-Host "Пользователь $Username перемещен в $TargetOU" -ForegroundColor Green
}
catch {
Write-Host "Ошибка при перемещении пользователя: $_" -ForegroundColor Red
}
}

# Функция для создания отчета о неактивных пользователях
function Get-InactiveADUsers {
param (
[Parameter(Mandatory=$false)]
[int]$DaysInactive = 90,

[Parameter(Mandatory=$false)]
[string]$ReportPath = "C:\Reports\InactiveUsers_$(Get-Date -Format 'yyyyMMdd').csv"
)

# Создаем директорию для отчетов, если она не существует
if (!(Test-Path (Split-Path $ReportPath -Parent))) {
New-Item -ItemType Directory -Path (Split-Path $ReportPath -Parent) -Force | Out-Null
}

$inactiveDate = (Get-Date).AddDays(-$DaysInactive)

try {
# Получаем неактивных пользователей
$inactiveUsers = Get-ADUser -Filter {LastLogonTimeStamp -lt $inactiveDate -and Enabled -eq $true} -Properties LastLogonTimeStamp, DisplayName, Department, Title, Manager, whenCreated |
Select-Object SamAccountName, DisplayName, Department, Title,
@{Name="Manager"; Expression={(Get-ADUser -Identity $_.Manager -Properties DisplayName).DisplayName}},
@{Name="LastLogon"; Expression={[DateTime]::FromFileTime($_.LastLogonTimeStamp)}},
@{Name="AccountAge"; Expression={(New-TimeSpan -Start $_.whenCreated -End (Get-Date)).Days}},
@{Name="DaysSinceLastLogon"; Expression={(New-TimeSpan -Start ([DateTime]::FromFileTime($_.LastLogonTimeStamp)) -End (Get-Date)).Days}}

# Экспортируем результаты в CSV
$inactiveUsers | Export-Csv -Path $ReportPath -NoTypeInformation -Encoding UTF8

Write-Host "Найдено $($inactiveUsers.Count) неактивных пользователей (более $DaysInactive дней)" -ForegroundColor Yellow
Write-Host "Отчет сохранен в $ReportPath" -ForegroundColor Green

return $inactiveUsers
}
catch {
Write-Host "Ошибка при создании отчета о неактивных пользователях: $_" -ForegroundColor Red
}
}

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

# Создание нового пользователя на основе шаблона
# $newUser = New-ADUserFromTemplate -FirstName "Иван" -LastName "Петров" -Department "IT" -Title "Системный администратор" -Manager "Сергей Иванов" -TemplateUser "jsmith"
# Write-Host "Создан пользователь: $($newUser.Username)" -ForegroundColor Green
# Write-Host "Пароль: $($newUser.Password)" -ForegroundColor Green
# Write-Host "Email: $($newUser.Email)" -ForegroundColor Green

# Блокировка пользователя
# Set-ADUserStatus -Username "jsmith" -Enabled $false

# Разблокировка пользователя
# Set-ADUserStatus -Username "jsmith" -Enabled $true

# Перемещение пользователя в другое подразделение
# Move-ADUserToOU -Username "jsmith" -TargetOU "OU=Disabled Users,DC=yourdomain,DC=com"

# Создание отчета о неактивных пользователях
# Get-InactiveADUsers -DaysInactive 60

Как использовать этот скрипт

  1. Сохраните скрипт в файл ADUserManager.ps1
  2. Настройте параметры в скрипте (домен, пути к OU и т.д.)
  3. Раскомментируйте примеры использования функций в конце скрипта или вызывайте функции из других скриптов

Этот скрипт особенно полезен, когда вам нужно:

  • Быстро создать нескольких пользователей с похожими параметрами
  • Массово обрабатывать учетные записи (блокировать/разблокировать, перемещать)
  • Регулярно создавать отчеты о неактивных пользователях

Преимущества этого скрипта

  • Автоматизация рутинных операций с учетными записями пользователей
  • Возможность создания пользователей на основе шаблонов
  • Генерация случайных паролей и автоматическое добавление в группы
  • Создание отчетов о неактивных пользователях для аудита безопасности

Скрипт №4: Автоматическое резервное копирование и ротация бэкапов

Резервное копирование — одна из самых важных задач администратора. Но часто бэкапы накапливаются и занимают все доступное пространство. Я создал скрипт, который не только выполняет резервное копирование, но и автоматически удаляет старые бэкапы, сохраняя только нужное количество копий.

Write-Log "Тип бэкапа: $backupType"
Write-Log "Создание директории для бэкапа: $backupPath"

# Создаем директорию для текущего бэкапа
New-Item -ItemType Directory -Path $backupPath -Force | Out-Null

# Копируем данные из исходных папок
foreach ($source in $sourceFolders) {
if (Test-Path $source) {
$folderName = Split-Path $source -Leaf
$destination = Join-Path -Path $backupPath -ChildPath $folderName

Write-Log "Копирование $source в $destination"

try {
# Используем robocopy для копирования файлов
$robocopyArgs = @(
$source,
$destination,
"/E", # Копировать подпапки, включая пустые
"/ZB", # Использовать режим перезапуска при ошибках, с резервным режимом доступа
"/R:3", # Количество повторов при ошибке
"/W:5", # Время ожидания между повторами (в секундах)
"/LOG+:$logPath", # Добавлять в лог-файл
"/NP", # Не показывать прогресс
"/NDL" # Не выводить список директорий
)

& robocopy $robocopyArgs

# Проверяем код возврата robocopy
# 0-7 считаются успешными, 8+ означают ошибки
if ($LASTEXITCODE -lt 8) {
Write-Log "Копирование $source завершено успешно"
}
else {
Write-Log "ПРЕДУПРЕЖДЕНИЕ: Копирование $source завершено с ошибками (код $LASTEXITCODE)"
}
}
catch {
Write-Log "ОШИБКА: Не удалось скопировать $source. Ошибка: $_"
}
}
else {
Write-Log "ПРЕДУПРЕЖДЕНИЕ: Исходная папка $source не существует"
}
}

# Архивируем бэкап
$zipPath = "$backupPath.zip"
Write-Log "Архивация бэкапа в $zipPath"

try {
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::CreateFromDirectory($backupPath, $zipPath)

# Удаляем неархивированную папку бэкапа
Remove-Item -Path $backupPath -Recurse -Force
Write-Log "Архивация завершена успешно"
}
catch {
Write-Log "ОШИБКА: Не удалось создать архив. Ошибка: $_"
}

# Ротация бэкапов
Write-Log "Начало ротации бэкапов"

# Функция для удаления старых бэкапов
function Remove-OldBackups {
param (
[string]$backupDir,
[int]$maxBackups
)

$backups = Get-ChildItem -Path $backupDir -Filter "Backup_*.zip" | Sort-Object LastWriteTime -Descending

if ($backups.Count -gt $maxBackups) {
$backupsToRemove = $backups | Select-Object -Skip $maxBackups

foreach ($backup in $backupsToRemove) {
Write-Log "Удаление старого бэкапа: $($backup.FullName)"
Remove-Item -Path $backup.FullName -Force
}
}
}

# Удаляем старые бэкапы в соответствии с настройками ротации
Remove-OldBackups -backupDir (Join-Path -Path $backupRoot -ChildPath "Daily") -maxBackups $maxDailyBackups
Remove-OldBackups -backupDir (Join-Path -Path $backupRoot -ChildPath "Weekly") -maxBackups $maxWeeklyBackups
Remove-OldBackups -backupDir (Join-Path -Path $backupRoot -ChildPath "Monthly") -maxBackups $maxMonthlyBackups

Write-Log "Ротация бэкапов завершена"
Write-Log "Процесс резервного копирования завершен успешно"

Как использовать этот скрипт

  1. Сохраните скрипт в файл BackupRotation.ps1
  2. Настройте параметры в начале скрипта (исходные папки, путь для бэкапов, настройки ротации)
  3. Создайте задание в планировщике задач Windows для ежедневного запуска скрипта

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

Преимущества этого скрипта

  • Автоматическое создание ежедневных, еженедельных и ежемесячных бэкапов
  • Интеллектуальная ротация бэкапов для экономии дискового пространства
  • Подробное логирование процесса резервного копирования
  • Использование robocopy для надежного копирования файлов с возможностью перезапуска при ошибках

Скрипт №5: Мониторинг и перезапуск проблемных служб

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

# Скрипт для мониторинга и перезапуска проблемных служб
# ServiceMonitor.ps1

# Настройки
$servicesToMonitor = @(
@{
ServerName = "Server1"
ServiceName = "W3SVC"
DisplayName = "IIS Web Server"
RestartDependentServices = $true
MaxRestartAttempts = 3
WaitTimeBetweenRestarts = 60 # секунды
},
@{
ServerName = "Server2"
ServiceName = "MSSQLSERVER"
DisplayName = "SQL Server"
RestartDependentServices = $true
MaxRestartAttempts = 2
WaitTimeBetweenRestarts = 120 # секунды
},
@{
ServerName = "Server3"
ServiceName = "Print Spooler"
DisplayName = "Print Spooler"
RestartDependentServices = $false
MaxRestartAttempts = 3
WaitTimeBetweenRestarts = 30 # секунды
}
)

$logPath = "C:\Logs\ServiceMonitor_$(Get-Date -Format 'yyyyMMdd').log"
$sendEmail = $true
$emailParams = @{
From = "monitoring@yourdomain.com"
To = "admin@yourdomain.com"
Subject = "Предупреждение: Перезапуск службы"
SmtpServer = "smtp.yourdomain.com"
}

# Создаем директорию для логов, если она не существует
if (!(Test-Path (Split-Path $logPath -Parent))) {
New-Item -ItemType Directory -Path (Split-Path $logPath -Parent) -Force | Out-Null
}

# Функция для записи в лог
function Write-Log {
param (
[string]$message,
[string]$level = "INFO"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"$timestamp - [$level] - $message" | Out-File -FilePath $logPath -Append

switch ($level) {
"ERROR" { Write-Host "$timestamp - [$level] - $message" -ForegroundColor Red }
"WARNING" { Write-Host "$timestamp - [$level] - $message" -ForegroundColor Yellow }
"SUCCESS" { Write-Host "$timestamp - [$level] - $message" -ForegroundColor Green }
default { Write-Host "$timestamp - [$level] - $message" }
}
}

Write-Log "Начало мониторинга служб"

# Функция для перезапуска службы
function Restart-ServiceWithRetry {
param (
[string]$serverName,
[string]$serviceName,
[string]$displayName,
[bool]$restartDependentServices,
[int]$maxAttempts,
[int]$waitTime
)

Write-Log "Проверка службы $displayName ($serviceName) на сервере $serverName"

try {
$service = Get-Service -ComputerName $serverName -Name $serviceName -ErrorAction Stop

if ($service.Status -ne "Running") {
Write-Log "Служба $displayName ($serviceName) на сервере $serverName не запущена (статус: $($service.Status))" -level "WARNING"

# Получаем зависимые службы, если нужно их перезапустить
$dependentServices = @()
if ($restartDependentServices) {
$dependentServices = Get-Service -ComputerName $serverName -Name $serviceName -DependentServices -ErrorAction SilentlyContinue
if ($dependentServices.Count -gt 0) {
Write-Log "Найдено $($dependentServices.Count) зависимых служб, которые будут остановлены"
}
}

# Попытки перезапуска
$attempt = 1
$success = $false

while (-not $success -and $attempt -le $maxAttempts) {
Write-Log "Попытка $attempt из $maxAttempts перезапустить службу $displayName ($serviceName)" -level "WARNING"

try {
# Останавливаем зависимые службы, если они есть
foreach ($depService in $dependentServices) {
if ($depService.Status -eq "Running") {
Write-Log "Остановка зависимой службы: $($depService.DisplayName)"
Stop-Service -InputObject $depService -Force -ErrorAction Stop
}
}

# Перезапускаем основную службу
if ($service.Status -eq "Running") {
Stop-Service -InputObject $service -Force -ErrorAction Stop
Start-Sleep -Seconds 5
}

Start-Service -InputObject $service -ErrorAction Stop
Start-Sleep -Seconds 5

# Проверяем статус после перезапуска
$service = Get-Service -ComputerName $serverName -Name $serviceName -ErrorAction Stop

if ($service.Status -eq "Running") {
Write-Log "Служба $displayName ($serviceName) успешно перезапущена" -level "SUCCESS"

# Запускаем зависимые службы
foreach ($depService in $dependentServices) {
Write-Log "Запуск зависимой службы: $($depService.DisplayName)"
Start-Service -InputObject $depService -ErrorAction SilentlyContinue
}

$success = $true

# Отправляем уведомление о перезапуске
if ($sendEmail) {
$body = @"
Служба $displayName ($serviceName) на сервере $serverName была успешно перезапущена.

Время перезапуска: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Попытка: $attempt из $maxAttempts

Это автоматическое уведомление.
"@

Send-MailMessage @emailParams -Body $body
Write-Log "Отправлено уведомление о перезапуске службы"
}
}
else {
Write-Log "Служба $displayName ($serviceName) не запустилась после перезапуска (статус: $($service.Status))" -level "ERROR"
$attempt++

if ($attempt -le $maxAttempts) {
Write-Log "Ожидание $waitTime секунд перед следующей попыткой"
Start-Sleep -Seconds $waitTime
}
}
}
catch {
Write-Log "Ошибка при перезапуске службы: $_" -level "ERROR"
$attempt++

if ($attempt -le $maxAttempts) {
Write-Log "Ожидание $waitTime секунд перед следующей попыткой"
Start-Sleep -Seconds $waitTime
}
}
}

# Если все попытки неудачны, отправляем уведомление об ошибке
if (-not $success -and $sendEmail) {
$body = @"
ОШИБКА: Не удалось перезапустить службу $displayName ($serviceName) на сервере $serverName после $maxAttempts попыток.

Время последней попытки: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Текущий статус: $($service.Status)

Требуется вмешательство администратора.
"@

Send-MailMessage @emailParams -Body $body -Priority High
Write-Log "Отправлено уведомление о неудачном перезапуске службы" -level "ERROR"
}
}
else {
Write-Log "Служба $displayName ($serviceName) на сервере $serverName работает нормально" -level "SUCCESS"
}
}
catch {
Write-Log "Ошибка при проверке службы $displayName ($serviceName) на сервере $serverName: $_" -level "ERROR"

if ($sendEmail) {
$body = @"
ОШИБКА: Не удалось проверить службу $displayName ($serviceName) на сервере $serverName.

Время проверки: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss")
Ошибка: $_

Требуется вмешательство администратора.
"@

Send-MailMessage @emailParams -Body $body -Priority High
Write-Log "Отправлено уведомление об ошибке проверки службы" -level "ERROR"
}
}
}

# Проверяем и перезапускаем службы
foreach ($service in $servicesToMonitor) {
Restart-ServiceWithRetry -serverName $service.ServerName `
-serviceName $service.ServiceName `
-displayName $service.DisplayName `
-restartDependentServices $service.RestartDependentServices `
-maxAttempts $service.MaxRestartAttempts `
-waitTime $service.WaitTimeBetweenRestarts
}

Write-Log "Мониторинг служб завершен"

Как использовать этот скрипт

  1. Сохраните скрипт в файл ServiceMonitor.ps1
  2. Настройте список служб для мониторинга в массиве $servicesToMonitor
  3. Настройте параметры логирования и отправки уведомлений
  4. Создайте задание в планировщике задач Windows для регулярного запуска скрипта

Я настроил запуск этого скрипта каждые 15 минут, чтобы быстро реагировать на остановку критически важных служб. Для менее важных служб можно установить более длительный интервал.

Преимущества этого скрипта

  • Автоматический мониторинг и перезапуск проблемных служб
  • Интеллектуальная обработка зависимых служб
  • Несколько попыток перезапуска с настраиваемыми интервалами
  • Подробное логирование и отправка уведомлений о проблемах
  • Возможность настройки параметров мониторинга для каждой службы индивидуально

Заключение: автоматизация — ключ к эффективности

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

PowerShell — невероятно мощный инструмент, который позволяет автоматизировать практически любую задачу в среде Windows Server. Начав с простых скриптов, вы постепенно можете создать целую библиотеку инструментов, которые будут работать за вас, пока вы занимаетесь более важными делами.

Представленные в этой статье скрипты — лишь малая часть того, что можно автоматизировать. Я рекомендую начать с них, адаптировать под свои нужды, а затем постепенно расширять свой арсенал автоматизации.

Помните, что хороший администратор — ленивый администратор, но в правильном смысле этого слова. Мы стремимся автоматизировать рутину не для того, чтобы меньше работать, а для того, чтобы сосредоточиться на задачах, которые действительно требуют нашего внимания и интеллекта.

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

Если вам понравилась эта статья и вы нашли в ней полезные идеи, пожалуйста, поставьте лайк и подпишитесь на мой канал. Я регулярно публикую материалы о системном администрировании, автоматизации и лучших практиках в IT. Ваша поддержка мотивирует меня создавать новый полезный контент!