|
Скрипт PowerShell (Backup-HyperV.ps1)
|
|
Этот скрипт делает следующее:
|
|
Создает папку для бэкапа с текущей датой.
|
|
Экспортирует все виртуальные машины в эту папку.
|
|
Удаляет папки с бэкапами, старше 180 дней.
|
|
Ведет лог действий.
|
|
Создайте файл C:\Scripts\Backup-HyperV.ps1 и вставьте в него этот код:
|
|
|
|
powershell
|
|
# ================= НАСТРОЙКИ =================
|
|
$BackupRootPath = "D:\HyperVBackups" # Путь, где будут храниться бэкапы
|
|
$LogFilePath = "C:\Scripts\BackupLog.txt" # Путь к файлу лога
|
|
$RetentionDays = 180 # Сколько дней хранить бэкапы
|
|
# =============================================
|
|
|
|
# Функция для записи в лог
|
|
function Write-Log {
|
|
param ([string]$Message)
|
|
$TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
$LogEntry = "[$TimeStamp] $Message"
|
|
Add-Content -Path $LogFilePath -Value $LogEntry
|
|
Write-Host $LogEntry
|
|
}
|
|
|
|
# Проверка прав администратора
|
|
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
|
Write-Log "ОШИБКА: Скрипт должен быть запущен от имени администратора!"
|
|
exit 1
|
|
}
|
|
|
|
# Проверка существования модуля Hyper-V
|
|
if (-not (Get-Module -ListAvailable -Name Hyper-V)) {
|
|
Write-Log "ОШИБКА: Модуль Hyper-V не найден. Убедитесь, что роль установлена."
|
|
exit 1
|
|
}
|
|
|
|
# Проверка пути бэкапа
|
|
if (-not (Test-Path -Path $BackupRootPath)) {
|
|
try {
|
|
New-Item -ItemType Directory -Path $BackupRootPath -Force | Out-Null
|
|
Write-Log "Папка бэкапов создана: $BackupRootPath"
|
|
} catch {
|
|
Write-Log "ОШИБКА: Не удалось создать папку бэкапов. $_"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
$DateStamp = Get-Date -Format "yyyy-MM-dd"
|
|
Write-Log "=== Начало задачи бэкапа ==="
|
|
Write-Log "Дата бэкапа: $DateStamp"
|
|
|
|
# Получение списка всех ВМ
|
|
$VMs = Get-VM
|
|
|
|
if ($VMs.Count -eq 0) {
|
|
Write-Log "Виртуальные машины не найдены."
|
|
} else {
|
|
foreach ($VM in $VMs) {
|
|
$VMName = $VM.Name
|
|
# Формируем имя папки: ИмяВМ_Дата
|
|
# Экспорт создаст внутри еще одну папку с именем ВМ, это стандартное поведение Export-VM
|
|
$ExportPath = "$BackupRootPath\$($VMName)_$DateStamp"
|
|
|
|
try {
|
|
Write-Log "Начало экспорта ВМ: $VMName"
|
|
# Export-VM автоматически создает чекпоинт на время бэкапа для консистентности данных
|
|
Export-VM -Name $VMName -Path $ExportPath -ErrorAction Stop
|
|
Write-Log "Успешно: ВМ $VMName экспортирована в $ExportPath"
|
|
}
|
|
catch {
|
|
Write-Log "ОШИБКА экспорта ВМ $VMName : $_"
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Log "=== Начало очистки старых бэкапов (старше $RetentionDays дней) ==="
|
|
|
|
# Получаем все папки в корне бэкапа
|
|
$BackupFolders = Get-ChildItem -Path $BackupRootPath -Directory
|
|
|
|
$DeletedCount = 0
|
|
foreach ($Folder in $BackupFolders) {
|
|
# Проверяем возраст папки по времени создания
|
|
if ($Folder.CreationTime -lt (Get-Date).AddDays(-$RetentionDays)) {
|
|
try {
|
|
Write-Log "Удаление старой папки: $($Folder.Name) (Дата: $($Folder.CreationTime))"
|
|
Remove-Item -Path $Folder.FullName -Recurse -Force
|
|
$DeletedCount++
|
|
}
|
|
catch {
|
|
Write-Log "ОШИБКА удаления папки $($Folder.Name): $_"
|
|
}
|
|
}
|
|
}
|
|
|
|
Write-Log "Удалено папок: $DeletedCount"
|
|
Write-Log "=== Задача бэкапа завершена ==="
|
|
Write-Log "-------------------------------------------"
|
|
|
|
|
|
|
|
|
|
2. Настройка Планировщика заданий (Task Scheduler)
|
|
Чтобы скрипт запускался автоматически в воскресенье в 03:00:
|
|
Откройте Планировщик заданий (taskschd.msc).
|
|
В меню справа выберите Создать задачу... (не "Создать простую задачу", чтобы были все опции).
|
|
Вкладка "Общие":
|
|
Имя: Hyper-V Weekly Backup.
|
|
Выберите Выполнять для всех пользователей.
|
|
Поставьте галочку Выполнять с наивысшими правами (Критично для доступа к Hyper-V!).
|
|
Выберите ОС: Windows 10/Server 2016 и выше (под вашу систему).
|
|
Вкладка "Триггеры":
|
|
Нажмите Создать.
|
|
Начало задачи: По расписанию.
|
|
Еженедельно.
|
|
Дни недели: Воскресенье.
|
|
Время: 03:00:00.
|
|
Нажмите ОК.
|
|
Вкладка "Действия":
|
|
Нажмите Создать.
|
|
Действие: Запуск программы.
|
|
Программа или сценарий: powershell.exe
|
|
|
|
Добавить аргументы:
|
|
|
|
text
|
|
-ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\Scripts\Backup-HyperV.ps1"
|
|
|
|
(Убедитесь, что путь к файлу совпадает с тем, где вы сохранили скрипт).
|
|
Нажмите ОК.
|
|
Вкладка "Условия":
|
|
Рекомендуется снять галочку Запускать задачу только при питании от электросети (если это сервер с ИБП, чтобы бэкап не пропустился при кратком перебое).
|
|
Снять галочку Запускать только при входе пользователя в сеть (если есть).
|
|
Нажмите ОК и введите пароль администратора.
|
|
3. Важные замечания и рекомендации
|
|
Дисковое пространство:
|
|
Бэкап через Export-VM создает полные копии дисков (VHDX).
|
|
Формула места: Размер_ВМ * Количество_ВМ * (180 дней / 7 дней).
|
|
Пример: Если у вас 5 ВМ по 100 ГБ, то за 180 дней (26 недель) вам понадобится: 5 * 100ГБ * 26 = 13 ТБ.
|
|
Убедитесь, что на диске D: достаточно места. Если место ограничено, рассмотрите сокращение срока хранения или использование дедупликации.
|
|
Производительность:
|
|
Export-VM создает временный чекпоинт (снимок) на работающей ВМ для обеспечения целостности данных. Это может немного замедлить ВМ во время бэкапа.
|
|
Время бэкапа зависит от скорости диска и объема данных. Если бэкап не успевает сделать к утру понедельника, это нормально, но следующие задания не должны накладываться (в данном случае они не наложатся, так как раз в неделю).
|
|
Тестирование:
|
|
Не ждите воскресенья. Запустите скрипт вручную в PowerShell (от имени администратора) один раз, чтобы проверить, что он работает, ВМ экспортируются и лог пишется.
|
|
Проверьте, что папки удаляются (можно временно изменить $RetentionDays = 0 для теста, а потом вернуть 180).
|
|
Восстановление:
|
|
Для восстановления используйте команду:
|
|
Import-VM -Path "D:\HyperVBackups\VMName_2023-10-29\VMName\Virtual Machines\GUID.xml"
|
|
Или через диспетчер Hyper-V -> Импорт виртуальной машины.
|
|
Уведомления:
|
|
В данном скрипте уведомления только в лог-файл. Если нужно получать письмо об ошибке на Email, потребуется дополнительная настройка SMTP в скрипте (командлет Send-MailMessage).
|
|
|
|
|
|
|
|
скрипт PowerShell, который автоматически создаст и настроит задачу в Планировщике заданий для еженедельного бэкапа виртуальных машин Hyper-V.
|
|
Инструкция по использованию
|
|
Сохраните скрипт бэкапа:
|
|
Убедитесь, что основной скрипт бэкапа (из предыдущего ответа) сохранен по пути:
|
|
C:\Scripts\Backup-HyperV.ps1
|
|
(Если путь другой, измените переменную $ScriptPath в скрипте ниже).
|
|
Создайте скрипт настройки:
|
|
Создайте файл C:\Scripts\Setup-BackupTask.ps1 и вставьте в него код ниже.
|
|
Запустите от имени Администратора:
|
|
Откройте PowerShell от имени администратора и выполните:
|
|
|
|
powershell
|
|
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
|
C:\Scripts\Setup-BackupTask.ps1
|
|
|
|
Скрипт создания задачи (Setup-BackupTask.ps1)
|
|
powershell
|
|
# ==============================================================================
|
|
# Скрипт создания задачи Планировщика для бэкапа Hyper-V
|
|
# Запускать СТРОГО от имени Администратора
|
|
# ==============================================================================
|
|
|
|
# --- НАСТРОЙКИ ---
|
|
$TaskName = "Hyper-V Weekly Backup"
|
|
$TaskPath = "\"
|
|
$ScriptPath = "C:\Scripts\Backup-HyperV.ps1" # Путь к скрипту бэкапа
|
|
$LogPath = "C:\Scripts\TaskCreation.log"
|
|
$RunAsUser = "SYSTEM" # Запуск от системы (надежнее для серверов)
|
|
# -----------------
|
|
|
|
function Write-Log {
|
|
param ([string]$Message)
|
|
$TimeStamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
|
|
$LogEntry = "[$TimeStamp] $Message"
|
|
Add-Content -Path $LogPath -Value $LogEntry
|
|
Write-Host $LogEntry -ForegroundColor Cyan
|
|
}
|
|
|
|
Write-Log "=== Начало настройки задачи планировщика ==="
|
|
|
|
# 1. Проверка прав администратора
|
|
if (-NOT ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator")) {
|
|
Write-Log "ОШИБКА: Скрипт должен быть запущен от имени администратора!"
|
|
exit 1
|
|
}
|
|
|
|
# 2. Проверка существования скрипта бэкапа
|
|
if (-not (Test-Path -Path $ScriptPath)) {
|
|
Write-Log "ОШИБКА: Скрипт бэкапа не найден по пути: $ScriptPath"
|
|
Write-Log "Пожалуйста, создайте скрипт бэкапа перед запуском этой настройки."
|
|
exit 1
|
|
}
|
|
|
|
# 3. Проверка модуля Планировщика
|
|
try {
|
|
$ScheduleService = New-Object -ComObject "Schedule.Service"
|
|
$ScheduleService.Connect()
|
|
Write-Log "Подключение к службе Планировщика заданий успешно."
|
|
} catch {
|
|
Write-Log "ОШИБКА: Не удалось подключиться к службе Планировщика. $_"
|
|
exit 1
|
|
}
|
|
|
|
# 4. Удаление старой задачи (если существует), чтобы пересоздать её чистой
|
|
$RootFolder = $ScheduleService.GetFolder($TaskPath)
|
|
try {
|
|
$OldTask = $RootFolder.GetTask($TaskName)
|
|
if ($OldTask) {
|
|
Write-Log "Обнаружена существующая задача '$TaskName'. Удаление..."
|
|
$RootFolder.DeleteTask($TaskName, 0)
|
|
Start-Sleep -Seconds 1
|
|
}
|
|
} catch {
|
|
# Задачи нет, это нормально
|
|
}
|
|
|
|
# 5. Создание новой задачи
|
|
Write-Log "Создание новой задачи '$TaskName'..."
|
|
$TaskDefinition = $ScheduleService.NewTask(0)
|
|
|
|
# --- Настройка принципов (Principals) ---
|
|
$TaskDefinition.Principal.UserId = $RunAsUser
|
|
$TaskDefinition.Principal.RunLevel = 1 # Highest (Наивысшие права)
|
|
$TaskDefinition.Principal.LogonType = 3 # S4U (Запуск без входа пользователя)
|
|
Write-Log "Настроено: Запуск от $RunAsUser с наивысшими правами."
|
|
|
|
# --- Настройка триггера (Triggers) ---
|
|
$Trigger = $TaskDefinition.Triggers.Create(1) # 1 = Weekly (Еженедельно)
|
|
$Trigger.StartBoundary = (Get-Date).Date.AddHours(3).ToString("yyyy-MM-ddTHH:mm:ss") # Сегодня в 3:00 (для инициализации)
|
|
$Trigger.DaysOfWeek = 64 # 64 = Sunday (Воскресенье)
|
|
$Trigger.WeeksInterval = 1
|
|
$Trigger.Enabled = $true
|
|
Write-Log "Настроено: Запуск каждое Воскресенье в 03:00."
|
|
|
|
# --- Настройка действия (Actions) ---
|
|
$Action = $TaskDefinition.Actions.Create(0) # 0 = Executable
|
|
$Action.Path = "powershell.exe"
|
|
# Аргументы: Bypass (обход политик), Hidden (скрытое окно), File (путь к скрипту)
|
|
$Action.Arguments = "-ExecutionPolicy Bypass -WindowStyle Hidden -File `"$ScriptPath`""
|
|
$Action.WorkingDirectory = "C:\Scripts"
|
|
Write-Log "Настроено: Запуск PowerShell скрипта $ScriptPath"
|
|
|
|
# --- Настройка условий (Settings) ---
|
|
$TaskDefinition.Settings.AllowHardTerminate = $true
|
|
$TaskDefinition.Settings.StartWhenAvailable = $true
|
|
$TaskDefinition.Settings.StopIfGoingOnBatteries = $false # Не останавливать при питании от батарей (важно для ИБП)
|
|
$TaskDefinition.Settings.IdleSettings.StopOnIdleEnd = $false
|
|
$TaskDefinition.Settings.ExecutionTimeLimit = "PT12H" # Лимит выполнения 12 часов
|
|
Write-Log "Настроено: Отключена остановка при работе от батарей, лимит времени 12ч."
|
|
|
|
# --- Регистрация задачи ---
|
|
try {
|
|
# 6 = Create or Update
|
|
$RootFolder.RegisterTaskDefinition($TaskName, $TaskDefinition, 6, $RunAsUser, $null, 3)
|
|
Write-Log "УСПЕШНО: Задача '$TaskName' создана и зарегистрирована."
|
|
} catch {
|
|
Write-Log "ОШИБКА при регистрации задачи: $_"
|
|
exit 1
|
|
}
|
|
|
|
# 6. Проверка и запуск теста (опционально)
|
|
Write-Log "Проверка статуса задачи..."
|
|
try {
|
|
$CreatedTask = $RootFolder.GetTask($TaskName)
|
|
Write-Log "Статус задачи: $($CreatedTask.State)"
|
|
|
|
# Предложение запустить тестовый бэкап сразу
|
|
Write-Host "`nХотите запустить тестовое выполнение задачи прямо сейчас? (Y/N)" -ForegroundColor Yellow
|
|
$response = Read-Host
|
|
|
|
if ($response -eq 'Y' -or $response -eq 'y') {
|
|
Write-Log "Запуск задачи на выполнение..."
|
|
$CreatedTask.Run("")
|
|
Write-Log "Задача запущена. Проверьте логи бэкапа через несколько минут."
|
|
} else {
|
|
Write-Log "Тестовый запуск пропущен. Задача начнёт работать в ближайшее воскресенье в 03:00."
|
|
}
|
|
} catch {
|
|
Write-Log "Не удалось получить доступ к созданной задаче: $_"
|
|
}
|
|
|
|
Write-Log "=== Настройка завершена ==="
|
|
|
|
|
|
|
|
Что делает этот скрипт:
|
|
Проверяет права: Убеждается, что вы запустили консоль от имени Администратора.
|
|
Очищает старое: Если задача с таким именем уже существует, она удаляется и создается заново (чтобы избежать конфликтов настроек).
|
|
Настраивает безопасность:
|
|
Запуск от имени SYSTEM (наиболее надежно для серверных задач).
|
|
Уровень прав: Наивысшие.
|
|
Тип входа: S4U (задача работает, даже если никто не залогинен в сервер).
|
|
Настраивает расписание: Каждое воскресенье в 03:00 ночи.
|
|
Настраивает питание: Запрещает остановку задачи, если сервер работает от ИБП (важно для длительных бэкапов).
|
|
Тест: После создания скрипт спросит, хотите ли вы запустить бэкап прямо сейчас для проверки.
|
|
Как проверить результат:
|
|
Откройте Планировщик заданий (taskschd.msc).
|
|
Найдите в списке задачу Hyper-V Weekly Backup.
|
|
Нажмите правой кнопкой -> Свойства.
|
|
Убедитесь, что на вкладке "Общие" стоит галочка "Выполнять с наивысшими правами".
|
|
Посмотрите вкладку "Журнал" (включите её, если отключена), чтобы видеть историю запусков.
|