💾 Автоматический бэкап виртуальных машин Hyper-V¶
Описание: Скрипт PowerShell для еженедельного экспорта ВМ Hyper-V с ротацией архивов и автоматической настройкой через Планировщик заданий.
📋 Компоненты решения¶
| Компонент | Функция | Путь |
|---|---|---|
Backup-HyperV.ps1 |
Экспорт ВМ, ротация бэкапов, логирование | C:\Scripts\ |
Setup-BackupTask.ps1 |
Автоматическая регистрация задачи в Планировщике | C:\Scripts\ |
| Планировщик заданий | Запуск каждую неделю в воскресенье в 03:00 | taskschd.msc |
🔧 Часть 1: Основной скрипт бэкапа¶
1.1 Создайте файл¶
# Путь для сохранения
C:\Scripts\Backup-HyperV.ps1
1.2 Содержимое Backup-HyperV.ps1
¶
# ================= НАСТРОЙКИ =================
$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
$ExportPath = "$BackupRootPath$($VMName)_$DateStamp"
try {
Write-Log "Начало экспорта ВМ: $VMName"
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 "-------------------------------------------"
1.3 Разрешите выполнение скриптов¶
# Разрешите выполнение скриптов (однократно)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Тестовый запуск (от имени Администратора!)¶
C:\Scripts\Backup-HyperV.ps1
D:\HyperVBackups\ и записи добавлены в C:\Scripts\BackupLog.txt
⏰ Часть 2: Настройка Планировщика заданий¶
Есть два способа: ручной (через GUI) или автоматический (через скрипт).
📌 Вариант А: Ручная настройка (через GUI)¶
- Откройте Планировщик заданий:
taskschd.msc - В меню справа выберите Создать задачу... (не "Создать простую задачу")
| Вкладка | Параметр | Значение |
|---|---|---|
| Общие | Имя задачи | Hyper-V Weekly Backup |
| Выполнять для | ✅ Всех пользователей | |
| Права | ✅ Выполнять с наивысшими правами | |
| ОС | Windows 10/Server 2016 и выше | |
| Триггеры | Начало задачи | По расписанию |
| Частота | Еженедельно | |
| Дни недели | Воскресенье | |
| Время | 03:00:00 | |
| Действия | Программа | powershell.exe |
| Аргументы | -ExecutionPolicy Bypass -WindowStyle Hidden -File "C:\Scripts\Backup-HyperV.ps1" |
|
| Условия | Питание | ❌ Снять "Запускать только от электросети" |
| Сеть | ❌ Снять "Запускать только при входе пользователя" |
🤖 Вариант Б: Автоматическая настройка (через скрипт)¶
Шаг 1: Создайте файл настройки¶
# Путь для сохранения
C:\Scripts\Setup-BackupTask.ps1
Шаг 2: Вставьте код¶
# ==============================================================================
# Скрипт создания задачи Планировщика для бэкапа 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"
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
$TaskDefinition.Principal.LogonType = 3
Write-Log "Настроено: Запуск от $RunAsUser с наивысшими правами."
--- Triggers ---¶
$Trigger = $TaskDefinition.Triggers.Create(1)
$Trigger.StartBoundary = (Get-Date).Date.AddHours(3).ToString("yyyy-MM-ddTHH:mm:ss")
$Trigger.DaysOfWeek = 64
$Trigger.WeeksInterval = 1
$Trigger.Enabled = $true
Write-Log "Настроено: Запуск каждое Воскресенье в 03:00."
--- Actions ---¶
$Action = $TaskDefinition.Actions.Create(0)
$Action.Path = "powershell.exe"
$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"
Write-Log "Настроено: Лимит времени 12ч, работа от ИБП разрешена."
--- Регистрация ---¶
try {
$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 "Задача запущена. Проверьте логи бэкапа через несколько минут."
}
} catch {
Write-Log "Не удалось получить доступ к созданной задаче: $_"
}
Write-Log "=== Настройка завершена ==="
Шаг 3: Запустите скрипт настройки¶
# Откройте PowerShell от имени администратора
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
C:\Scripts\Setup-BackupTask.ps1
⚠️ Часть 3: Важные замечания и рекомендации¶
💿 Дисковое пространство¶
Бэкап через Export-VM создает полные копии дисков (VHDX).
Формула расчёта: Размер_ВМ × Количество_ВМ × (180 дней / 7 дней)
Пример: 5 ВМ по 100 ГБ × 26 недель = 13 ТБ за 180 дней
✅ Убедитесь, что на диске D: достаточно места. При ограничении рассмотрите:
- Сокращение срока хранения (
$RetentionDays) - Использование дедупликации
- Выгрузку старых бэкапов в облако
⚡ Производительность¶
-
Export-VMсоздает временный чекпоинт на работающей ВМ для целостности данных - Это может немного замедлить ВМ во время бэкапа
- Время бэкапа зависит от скорости диска и объема данных
- Задания не накладываются (запуск раз в неделю)
🧪 Тестирование¶
- Не ждите воскресенья — запустите скрипт вручную один раз
- Проверьте, что ВМ экспортируются и лог пишется
- Для проверки удаления: временно установите
$RetentionDays = 0, затем верните180
🔄 Восстановление¶
# Через PowerShell
Import-VM -Path "D:\HyperVBackups\VMName_2023-10-29\VMName\Virtual Machines\GUID.xml"
Или через диспетчер Hyper-V → Импорт виртуальной машины¶
📧 Уведомления¶
В данном скрипте уведомления только в лог-файл. Для email-уведомлений добавьте в скрипт:
Send-MailMessage -From "backup@domain.com" -To "admin@domain.com" `
-Subject "Hyper-V Backup Status" -Body $LogContent -SmtpServer "smtp.domain.com"
✅ Часть 4: Проверка результата¶
- Откройте Планировщик заданий:
taskschd.msc - Найдите задачу Hyper-V Weekly Backup
- ПКМ → Свойства
- Проверьте вкладку Общие: ✅ "Выполнять с наивысшими правами"
- Включите вкладку Журнал (если отключена) для истории запусков
📊 Полезные команды для проверки¶
# Просмотр логов бэкапа
Get-Content C:\Scripts\BackupLog.txt -Tail 50
Просмотр логов создания задачи¶
Get-Content C:\Scripts\TaskCreation.log -Tail 50
Проверка статуса задачи¶
Get-ScheduledTask -TaskName "Hyper-V Weekly Backup"
Запуск задачи вручную¶
Start-ScheduledTask -TaskName "Hyper-V Weekly Backup"
Просмотр истории выполнения¶
Get-ScheduledTaskInfo -TaskName "Hyper-V Weekly Backup"
Проверка папок бэкапа¶
Get-ChildItem D:\HyperVBackups -Directory | Sort-Object CreationTime -Descending | Select-Object -First 10
📁 Структура после настройки¶
C:\Scripts\
├── Backup-HyperV.ps1 # Основной скрипт бэкапа
├── Setup-BackupTask.ps1 # Скрипт настройки задачи
├── BackupLog.txt # Лог выполнения бэкапов
└── TaskCreation.log # Лог создания задачи
D:\HyperVBackups
├── VM1_2026-06-15
│ └── VM1
│ ├── Virtual Machines
│ └── ...
├── VM2_2026-06-15
└── ...
🔐 Требования безопасности¶
- Оба скрипта требуют запуска от имени Администратора
- Задача выполняется от SYSTEM (наиболее надежно для серверов)
- Тип входа S4U — задача работает без входа пользователя
- Храните скрипты в защищенной папке с ограниченным доступом
Обновлено Redmine Admin 5 месяца назад · 1 изменени(я, ий)