Files
PowerShell_Scrips/MailBoxBackup_TB.ps1

448 lines
20 KiB
PowerShell
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<#
.SYNOPSIS
Скрипт для закрытия Thunderbird и резервного копирования профиля
.DESCRIPTION
Выполняет поиск процессов Thunderbird, завершает их, ожидает полного закрытия
и создает копию папки профиля в указанное место. Автоматически создает папки назначения.
.NOTES
Author: RobinHood
Date: $(Get-Date -Format 'dd.MM.yyyy')
#>
# === НАСТРОЙКИ ===
$ThunderbirdProcessName = "thunderbird" # Имя процесса без .exe
$SourceProfilePath = "$env:APPDATA\Thunderbird\Profiles" # Стандартный путь к профилям
# Настройка пути для резервной копии (папка создастся автоматически)
$BackupRoot = "E:\Backups\Thunderbird" # Корневая папка для бэкапов можно использовать сетевую папку "\\server\share\"
$BackupDestination = Join-Path -Path $BackupRoot -ChildPath (Get-Date -Format 'yyyy-MM-dd') # Папка с датой
$MaxWaitTimeSeconds = 30 # Максимальное время ожидания закрытия Thunderbird (в секундах)
$CopyRetryCount = 3 # Количество попыток копирования при ошибках
$MinFreeSpaceMB = 500 # Минимальное свободное место на диске назначения (МБ)
# =================
# Функция для записи лога
function Write-Log {
param(
[string]$Message,
[string]$Type = "INFO"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
# Цвета для разных типов сообщений
switch ($Type) {
"SUCCESS" { $color = "Green" }
"ERROR" { $color = "Red" }
"WARNING" { $color = "Yellow" }
"INFO" { $color = "Cyan" }
default { $color = "White" }
}
Write-Host "[$timestamp] [$Type] " -NoNewline
Write-Host "$Message" -ForegroundColor $color
}
# Функция для создания папки назначения
function New-BackupFolder {
param(
[string]$FolderPath
)
try {
if (-not (Test-Path $FolderPath)) {
Write-Log "Папка назначения не существует. Создание: $FolderPath" -Type "INFO"
# Создаем все папки в пути рекурсивно
New-Item -Path $FolderPath -ItemType Directory -Force | Out-Null
Write-Log "Папка успешно создана" -Type "SUCCESS"
} else {
Write-Log "Папка назначения уже существует: $FolderPath" -Type "INFO"
}
return $true
} catch {
Write-Log "ОШИБКА при создании папки назначения: $_" -Type "ERROR"
return $false
}
}
# Функция для проверки свободного места на диске
function Test-FreeSpace {
param(
[string]$Path,
[long]$RequiredSpaceMB = 500
)
try {
# Получаем корневой диск из пути
$rootPath = Split-Path -Path $Path -Qualifier
if ([string]::IsNullOrEmpty($rootPath)) {
$rootPath = $Path[0] + ":"
}
$drive = Get-PSDrive -Name $rootPath[0] -ErrorAction Stop
$freeSpaceMB = [math]::Round($drive.Free / 1MB, 2)
Write-Log "Свободное место на диске $rootPath : $freeSpaceMB MB" -Type "INFO"
if ($drive.Free -lt ($RequiredSpaceMB * 1MB)) {
Write-Log "НЕДОСТАТОЧНО МЕСТА! Требуется: $RequiredSpaceMB MB, доступно: $freeSpaceMB MB" -Type "ERROR"
return $false
}
Write-Log "Места достаточно: $freeSpaceMB MB свободно" -Type "SUCCESS"
return $true
} catch {
Write-Log "Не удалось проверить свободное место: $_" -Type "WARNING"
return $true # Продолжаем, если не удалось проверить
}
}
# Функция для проверки, запущен ли Thunderbird
function Test-ThunderbirdRunning {
$processes = Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue
return ($processes.Count -gt 0)
}
# Функция для закрытия Thunderbird
function Stop-ThunderbirdGracefully {
Write-Log "Поиск запущенных процессов Thunderbird..." -Type "INFO"
if (Test-ThunderbirdRunning) {
$processCount = (Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue).Count
Write-Log "Найдено процессов Thunderbird: $processCount. Попытка корректного завершения..." -Type "WARNING"
# Пытаемся закрыть окна Thunderbird (более мягкий способ)
$thunderbirdWindows = Get-Process -Name $ThunderbirdProcessName | Where-Object { $_.MainWindowTitle -ne "" }
if ($thunderbirdWindows) {
foreach ($proc in $thunderbirdWindows) {
$windowTitle = if ($proc.MainWindowTitle) { $proc.MainWindowTitle } else { "Без заголовка" }
Write-Log "Закрытие окна: $windowTitle"
$proc.CloseMainWindow() | Out-Null
}
} else {
Write-Log "Нет видимых окон Thunderbird, но процессы запущены" -Type "WARNING"
}
# Даем время на закрытие
$waitCounter = 0
Write-Host ""
while ((Test-ThunderbirdRunning) -and ($waitCounter -lt $MaxWaitTimeSeconds)) {
Start-Sleep -Seconds 1
$waitCounter++
# Простой индикатор прогресса
$progress = [math]::Round(($waitCounter / $MaxWaitTimeSeconds) * 100)
Write-Host "`rОжидание закрытия Thunderbird: $progress% " -NoNewline
}
Write-Host ""
# Если всё ещё запущен - принудительно завершаем
if (Test-ThunderbirdRunning) {
Write-Log "Thunderbird не закрылся за отведенное время. Принудительное завершение..." -Type "ERROR"
$remainingProcesses = Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue
foreach ($proc in $remainingProcesses) {
try {
Stop-Process -Id $proc.Id -Force
Write-Log "Процесс PID $($proc.Id) принудительно завершен" -Type "WARNING"
} catch {
Write-Log "Не удалось завершить процесс PID $($proc.Id): $_" -Type "ERROR"
}
}
Start-Sleep -Seconds 3
} else {
Write-Log "Thunderbird успешно закрыт" -Type "SUCCESS"
}
} else {
Write-Log "Thunderbird не запущен" -Type "INFO"
}
# Финальная проверка
if (Test-ThunderbirdRunning) {
Write-Log "ВНИМАНИЕ: Некоторые процессы Thunderbird всё еще запущены!" -Type "ERROR"
# Показываем оставшиеся процессы
Get-Process -Name $ThunderbirdProcessName -ErrorAction SilentlyContinue |
ForEach-Object { Write-Log " - PID: $($_.Id), Запущен: $($_.StartTime)" -Type "ERROR" }
Write-Log "Продолжаем копирование, но некоторые файлы могут быть заблокированы" -Type "WARNING"
return $false
}
return $true
}
# Функция для получения размера папки
function Get-FolderSize {
param([string]$Path)
try {
if (Test-Path $Path) {
$size = (Get-ChildItem $Path -Recurse -ErrorAction SilentlyContinue |
Measure-Object -Property Length -Sum -ErrorAction SilentlyContinue).Sum
if ($size) {
return [math]::Round($size / 1MB, 2)
}
}
} catch {
# Игнорируем ошибки при подсчете размера
}
return 0
}
# Функция для копирования профиля
function Copy-ThunderbirdProfile {
param(
[string]$Source,
[string]$Destination
)
Write-Log "Начало копирования профиля..." -Type "INFO"
Write-Log " Источник: $Source" -Type "INFO"
Write-Log " Назначение: $Destination" -Type "INFO"
# Проверяем существование исходной папки
if (-not (Test-Path $Source)) {
Write-Log "Папка профиля НЕ НАЙДЕНА: $Source" -Type "ERROR"
return $false
}
# Получаем размер исходной папки
$sourceSizeMB = Get-FolderSize -Path $Source
Write-Log "Размер исходной папки: ~$sourceSizeMB MB" -Type "INFO"
# Проверяем свободное место
$requiredSpace = [math]::Max($MinFreeSpaceMB, $sourceSizeMB + 100) # +100 МБ запас
if (-not (Test-FreeSpace -Path $Destination -RequiredSpaceMB $requiredSpace)) {
Write-Log "Недостаточно места для копирования. Операция отменена." -Type "ERROR"
return $false
}
# СОЗДАЕМ ПАПКУ НАЗНАЧЕНИЯ (ключевое изменение!)
if (-not (New-BackupFolder -FolderPath $Destination)) {
return $false
}
# Получаем список всех профилей в папке Profiles
$profiles = Get-ChildItem -Path $Source -Directory -ErrorAction SilentlyContinue
if ($profiles.Count -eq 0) {
Write-Log "В папке Profiles не найдено профилей!" -Type "WARNING"
Write-Log "Проверяем наличие файлов напрямую..." -Type "INFO"
# Если нет подпапок, возможно копируем содержимое самой папки Profiles
$files = Get-ChildItem -Path $Source -File
if ($files.Count -gt 0) {
Write-Log "Найдены файлы в корне папки Profiles, копируем их" -Type "INFO"
# Пытаемся скопировать содержимое папки Profiles
$robocopyArgs = @(
"`"$Source`"",
"`"$Destination`"",
"/E", # Копировать подпапки, включая пустые
"/COPY:DAT", # Копировать данные, атрибуты, временные метки
"/R:3", # Retry count
"/W:5", # Wait time
"/NP", # No Progress
"/NJH", # No Job Header
"/NJS" # No Job Summary
)
try {
$robocopyResult = Start-Process -FilePath "robocopy.exe" -ArgumentList $robocopyArgs -Wait -PassThru -NoNewWindow
if ($robocopyResult.ExitCode -ge 8) {
throw "Robocopy завершился с ошибкой. Код: $($robocopyResult.ExitCode)"
}
Write-Log "Файлы скопированы" -Type "SUCCESS"
return $true
} catch {
Write-Log "Ошибка при копировании: $_" -Type "ERROR"
return $false
}
} else {
Write-Log "Папка Profiles пуста" -Type "WARNING"
return $false
}
}
# Копируем каждый профиль с повторами при ошибках
$attempt = 1
$allCopied = $false
while ($attempt -le $CopyRetryCount -and -not $allCopied) {
try {
Write-Log "Попытка копирования #$attempt из $CopyRetryCount..."
$successCount = 0
$failCount = 0
foreach ($profile in $profiles) {
$profileName = $profile.Name
$sourceProfilePath = $profile.FullName
$destProfilePath = Join-Path -Path $Destination -ChildPath $profileName
Write-Log " Копирование профиля: $profileName"
# Используем Robocopy для надежного копирования
$robocopyArgs = @(
"`"$sourceProfilePath`"",
"`"$destProfilePath`"",
"/MIR", # Mirror (точная копия)
"/R:3", # Retry count (3 повтора при ошибке)
"/W:5", # Wait time between retries (5 сек)
"/NP", # No Progress (чтобы не засорять вывод)
"/NJH", # No Job Header
"/NJS" # No Job Summary
)
Write-Log " Команда: robocopy $sourceProfilePath $destProfilePath /MIR /R:3 /W:5" -Type "INFO"
$robocopyResult = Start-Process -FilePath "robocopy.exe" -ArgumentList $robocopyArgs -Wait -PassThru -NoNewWindow
# Robocopy возвращает коды: 0-7 - успех, 8+ - ошибка
if ($robocopyResult.ExitCode -ge 8) {
Write-Log " Robocopy завершился с ошибкой. Код: $($robocopyResult.ExitCode)" -Type "ERROR"
$failCount++
} else {
Write-Log " Профиль $profileName скопирован (код: $($robocopyResult.ExitCode))" -Type "SUCCESS"
$successCount++
}
}
if ($failCount -eq 0) {
$allCopied = $true
Write-Log "Все профили успешно скопированы! ($successCount профилей)" -Type "SUCCESS"
} else {
Write-Log "Скопировано: $successCount, Ошибок: $failCount" -Type "WARNING"
if ($attempt -lt $CopyRetryCount) {
Write-Log "Повторная попытка через 5 секунд..." -Type "INFO"
Start-Sleep -Seconds 5
}
}
} catch {
Write-Log "Ошибка при копировании (попытка $attempt): $_" -Type "ERROR"
$failCount = $profiles.Count
}
$attempt++
}
return ($allCopied -or ($successCount -gt 0))
}
# Функция для создания информационного файла о бэкапе
function New-BackupInfoFile {
param(
[string]$BackupPath,
[string]$SourcePath
)
try {
$infoFile = Join-Path -Path $BackupPath -ChildPath "_backup_info.txt"
$computerInfo = @(
"# Информация о резервной копии Thunderbird",
"Дата создания: $(Get-Date -Format 'dd.MM.yyyy HH:mm:ss')",
"Компьютер: $env:COMPUTERNAME",
"Пользователь: $env:USERNAME",
"Исходная папка: $SourcePath",
"Размер исходной папки: $(Get-FolderSize -Path $SourcePath) MB",
"---",
"Создано скриптом: $($MyInvocation.MyCommand.Path)"
)
$computerInfo | Out-File -FilePath $infoFile -Encoding UTF8
Write-Log "Создан информационный файл: _backup_info.txt" -Type "INFO"
} catch {
Write-Log "Не удалось создать информационный файл: $_" -Type "WARNING"
}
}
# === ОСНОВНАЯ ЧАСТЬ СКРИПТА ===
Clear-Host
Write-Log "===================================================" -Type "INFO"
Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ ПРОФИЛЯ THUNDERBIRD" -Type "INFO"
Write-Log "===================================================" -Type "INFO"
Write-Log ""
# 1. Проверяем и создаем корневую папку для бэкапов, если нужно
Write-Log "Проверка корневой папки для бэкапов: $BackupRoot" -Type "INFO"
New-BackupFolder -FolderPath $BackupRoot | Out-Null
Write-Log ""
# 2. Проверяем наличие исходной папки
if (-not (Test-Path $SourceProfilePath)) {
Write-Log "Исходная папка не найдена: $SourceProfilePath" -Type "ERROR"
Write-Log "Проверьте путь и права доступа" -Type "INFO"
exit 1
}
# 3. Закрываем Thunderbird
Write-Log "Этап 1: Закрытие Thunderbird" -Type "INFO"
Write-Log "------------------------------" -Type "INFO"
$closed = Stop-ThunderbirdGracefully
Write-Log ""
# 4. Небольшая пауза для гарантии
Start-Sleep -Seconds 2
# 5. Копируем профили
Write-Log "Этап 2: Копирование профилей" -Type "INFO"
Write-Log "-----------------------------" -Type "INFO"
$copySuccess = Copy-ThunderbirdProfile -Source $SourceProfilePath -Destination $BackupDestination
Write-Log ""
# 6. Создаем информационный файл (если копирование успешно)
if ($copySuccess) {
New-BackupInfoFile -BackupPath $BackupDestination -SourcePath $SourceProfilePath
# 7. Итог
Write-Log "===================================================" -Type "SUCCESS"
Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ УСПЕШНО ЗАВЕРШЕНО" -Type "SUCCESS"
Write-Log "===================================================" -Type "SUCCESS"
Write-Log "Папка с резервной копией: $BackupDestination" -Type "INFO"
# Показываем содержимое папки с бэкапом
if (Test-Path $BackupDestination) {
$items = Get-ChildItem -Path $BackupDestination
Write-Log "Содержимое папки с бэкапом:" -Type "INFO"
foreach ($item in $items) {
$itemSize = if ($item.PSIsContainer) {
"<папка>"
} else {
"$([math]::Round($item.Length / 1KB, 2)) KB"
}
Write-Log " - $($item.Name) ($itemSize)" -Type "INFO"
}
}
# Предложение открыть папку
$openFolder = Read-Host "`nОткрыть папку с резервной копией? (Y/N)"
if ($openFolder -eq "Y" -or $openFolder -eq "y") {
Invoke-Item $BackupDestination
}
} else {
Write-Log "===================================================" -Type "ERROR"
Write-Log " РЕЗЕРВНОЕ КОПИРОВАНИЕ ЗАВЕРШИЛОСЬ С ОШИБКАМИ" -Type "ERROR"
Write-Log "===================================================" -Type "ERROR"
exit 1
}
# Для запуска с параметрами командной строки (раскомментируйте если нужно):
# param(
# [string]$BackupPath = "D:\Backups\Thunderbird\$(Get-Date -Format 'yyyy-MM-dd')"
# )
# $BackupDestination = $BackupPath