Update install-AdHoc-Backup.ps1
This commit is contained in:
parent
0a1d4275ee
commit
b2b25a5d64
@ -1,61 +1,458 @@
|
||||
# install-AdHoc-Backup.ps1
|
||||
# Uso tipico:
|
||||
# iwr -useb https://<il-tuo-raw-url>/install-AdHoc-Backup.ps1 | iex
|
||||
# Opzioni:
|
||||
# ... } -ForceConfig # sovrascrive backup.conf se già esiste
|
||||
# ... } -NoRun # installa senza eseguire il backup
|
||||
# ... } -AdHocArgs '-WhatIf' # argomenti aggiuntivi da passare allo script di backup
|
||||
<# =====================================================================
|
||||
install-AdHoc-Backup.ps1
|
||||
- Menù interattivo:
|
||||
1) Installa lo script e l'ambiente
|
||||
2) Crea pianificazione (Scheduled Task)
|
||||
3) Modifica configurazione (backup.conf) con guida in italiano
|
||||
|
||||
Requisiti:
|
||||
- Eseguire come Amministratore per creare la pianificazione con SYSTEM.
|
||||
- Tenere nella stessa cartella:
|
||||
- AdHoc-Backup.ps1
|
||||
- backup.conf
|
||||
|
||||
Note:
|
||||
- Rispettate le chiavi del tuo backup.conf e il suo "manuale" (liste con |,
|
||||
commenti con #, ecc.). Alcune spiegazioni sono riportate nei prompt.
|
||||
- Rclone: viene posizionato in $BackupRoot\RClone\rclone.exe. Se non presente,
|
||||
lo script prova a copiarlo da installazioni esistenti o può scaricarlo.
|
||||
===================================================================== #>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$InstallDir = 'C:\polo\scripts',
|
||||
[string]$ScriptUrl = 'https://gitea.poloinformatico.it/Mattia/Backup-AdHoc/raw/branch/main/AdHoc_Backup.ps1',
|
||||
[string]$ConfigUrl = 'https://gitea.poloinformatico.it/Mattia/Backup-AdHoc/raw/branch/main/backup.conf',
|
||||
[switch]$ForceConfig,
|
||||
[switch]$NoRun,
|
||||
[string[]]$AdHocArgs
|
||||
[string]$ConfigPath, # Facoltativo: percorso a backup.conf (default: .\backup.conf)
|
||||
[string]$BackupRootOverride # Facoltativo: forza BackupRoot (altrimenti legge da backup.conf)
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 -bor [Net.SecurityProtocolType]::Tls13 } catch {}
|
||||
Set-StrictMode -Version Latest
|
||||
|
||||
# 1) Crea cartella di destinazione
|
||||
if (-not (Test-Path -LiteralPath $InstallDir)) {
|
||||
New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
|
||||
function Write-Title($text) {
|
||||
Write-Host ""
|
||||
Write-Host "=== $text ===" -ForegroundColor Cyan
|
||||
}
|
||||
|
||||
# 2) Scarica i due file
|
||||
$ScriptPath = Join-Path $InstallDir 'AdHoc-Backup.ps1' # <- nome finale con trattino
|
||||
$ConfigPath = Join-Path $InstallDir 'backup.conf'
|
||||
|
||||
Write-Host "Scarico script: $ScriptUrl -> $ScriptPath"
|
||||
Invoke-WebRequest -UseBasicParsing -Uri $ScriptUrl -OutFile $ScriptPath
|
||||
|
||||
if ((-not (Test-Path -LiteralPath $ConfigPath)) -or $ForceConfig.IsPresent) {
|
||||
Write-Host "Scarico config: $ConfigUrl -> $ConfigPath"
|
||||
Invoke-WebRequest -UseBasicParsing -Uri $ConfigUrl -OutFile $ConfigPath
|
||||
} else {
|
||||
Write-Host "Config già presente, non sovrascrivo (usa -ForceConfig per forzare)."
|
||||
function Pause-Enter($msg="Premi INVIO per continuare...") {
|
||||
Write-Host ""
|
||||
Read-Host $msg | Out-Null
|
||||
}
|
||||
|
||||
# 3) Sblocca eventuali zone mark
|
||||
foreach ($p in @($ScriptPath, $ConfigPath)) {
|
||||
if (Test-Path -LiteralPath $p) {
|
||||
Unblock-File -LiteralPath $p -ErrorAction SilentlyContinue
|
||||
}
|
||||
function Read-Default($prompt, $default) {
|
||||
if ([string]::IsNullOrEmpty($default)) {
|
||||
return Read-Host "$prompt"
|
||||
} else {
|
||||
$ans = Read-Host "$prompt [$default]"
|
||||
if ([string]::IsNullOrEmpty($ans)) { return $default }
|
||||
return $ans
|
||||
}
|
||||
}
|
||||
|
||||
# 4) Esegui lo script (se non esplicitamente evitato)
|
||||
if (-not $NoRun.IsPresent) {
|
||||
Write-Host "Avvio: $ScriptPath"
|
||||
# Se il tuo AdHoc-Backup richiede la config, la passiamo in modo esplicito:
|
||||
$argsToPass = @()
|
||||
if (Test-Path -LiteralPath $ConfigPath) {
|
||||
$argsToPass += @('-Config', $ConfigPath)
|
||||
}
|
||||
if ($AdHocArgs) {
|
||||
$argsToPass += $AdHocArgs
|
||||
}
|
||||
|
||||
# Esegui in un processo figlio con ExecutionPolicy limitata al processo
|
||||
& powershell -NoProfile -ExecutionPolicy Bypass -File $ScriptPath @argsToPass
|
||||
function Read-YesNo($prompt, [bool]$default=$true) {
|
||||
$defTxt = if ($default) { "S/n" } else { "s/N" }
|
||||
while ($true) {
|
||||
$ans = Read-Host "$prompt ($defTxt)"
|
||||
if ([string]::IsNullOrWhiteSpace($ans)) { return $default }
|
||||
switch ($ans.ToLower()) {
|
||||
's' { return $true }
|
||||
'si' { return $true }
|
||||
'y' { return $true }
|
||||
'n' { return $false }
|
||||
'no' { return $false }
|
||||
default { Write-Host "Inserisci 's' o 'n'." -ForegroundColor Yellow }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Test-Admin {
|
||||
$id = [Security.Principal.WindowsIdentity]::GetCurrent()
|
||||
$p = New-Object Security.Principal.WindowsPrincipal($id)
|
||||
return $p.IsInRole([Security.Principal.WindowsBuiltinRole]::Administrator)
|
||||
}
|
||||
|
||||
# ---------- Config I/O (preserva commenti e ordine) ----------
|
||||
class ConfigDoc {
|
||||
[System.Collections.Generic.List[string]]$Lines
|
||||
[hashtable]$Map
|
||||
[hashtable]$Index
|
||||
ConfigDoc() {
|
||||
$this.Lines = [System.Collections.Generic.List[string]]::new()
|
||||
$this.Map = @{}
|
||||
$this.Index = @{}
|
||||
}
|
||||
}
|
||||
|
||||
function Load-Config([string]$path) {
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
throw "File di configurazione non trovato: $path"
|
||||
}
|
||||
$doc = [ConfigDoc]::new()
|
||||
$raw = Get-Content -LiteralPath $path -Raw -Encoding UTF8 -ErrorAction Stop
|
||||
foreach ($ln in ($raw -split "`r?`n", [System.StringSplitOptions]::None)) {
|
||||
$doc.Lines.Add($ln)
|
||||
}
|
||||
for ($i=0; $i -lt $doc.Lines.Count; $i++) {
|
||||
$line = $doc.Lines[$i]
|
||||
if ($line -match '^\s*#') { continue }
|
||||
if ($line -match '^\s*([A-Za-z0-9_]+)\s*=(.*)$') {
|
||||
$k = $matches[1]
|
||||
$v = ($matches[2]).Trim()
|
||||
$doc.Map[$k] = $v
|
||||
$doc.Index[$k] = $i
|
||||
}
|
||||
}
|
||||
return $doc
|
||||
}
|
||||
|
||||
function Save-Config([ConfigDoc]$doc, [string]$path) {
|
||||
$backup = "$path.bak_$(Get-Date -Format 'yyyyMMdd_HHmmss')"
|
||||
Copy-Item -LiteralPath $path -Destination $backup -Force -ErrorAction Stop
|
||||
|
||||
# Riscrivo solo le linee delle chiavi (preservo commenti/righe vuote)
|
||||
foreach ($k in $doc.Map.Keys) {
|
||||
$v = [string]$doc.Map[$k]
|
||||
if ($doc.Index.ContainsKey($k)) {
|
||||
$idx = [int]$doc.Index[$k]
|
||||
$doc.Lines[$idx] = "$k=$v"
|
||||
} else {
|
||||
# Aggiungo nuove chiavi alla fine
|
||||
if (-not $doc.Lines[$doc.Lines.Count-1].StartsWith('# AGGIUNTE AUTO')) {
|
||||
$doc.Lines.Add("")
|
||||
$doc.Lines.Add("# AGGIUNTE AUTO (create da install-AdHoc-Backup.ps1)")
|
||||
}
|
||||
$doc.Lines.Add("$k=$v")
|
||||
}
|
||||
}
|
||||
[IO.File]::WriteAllLines($path, $doc.Lines, [Text.UTF8Encoding]::new($false))
|
||||
return $backup
|
||||
}
|
||||
|
||||
function Set-ConfigValue([ConfigDoc]$doc, [string]$key, [string]$value) {
|
||||
$doc.Map[$key] = $value
|
||||
}
|
||||
|
||||
# ---------- Validatori ----------
|
||||
function Validate-IntNonNeg($valText, [int]$min=0, [int]$max=[int]::MaxValue) {
|
||||
if ([string]::IsNullOrWhiteSpace($valText)) { return $null } # mantieni
|
||||
[int]$n = 0
|
||||
if (-not [int]::TryParse($valText, [ref]$n)) { throw "Numero non valido." }
|
||||
if ($n -lt $min -or $n -gt $max) { throw "Valore fuori range ($min..$max)." }
|
||||
return $n
|
||||
}
|
||||
function Validate-Port($valText) { return (Validate-IntNonNeg $valText 1 65535) }
|
||||
|
||||
function Normalize-BoolText($text, [bool]$current) {
|
||||
if ([string]::IsNullOrWhiteSpace($text)) { return $current }
|
||||
switch ($text.Trim().ToLower()) {
|
||||
'true' { return $true }
|
||||
'false' { return $false }
|
||||
's' { return $true }
|
||||
'si' { return $true }
|
||||
'y' { return $true }
|
||||
'n' { return $false }
|
||||
'no' { return $false }
|
||||
default { throw "Valore non valido. Usa: true/false oppure s/n" }
|
||||
}
|
||||
}
|
||||
|
||||
function Read-Secret($prompt, $currentPlain) {
|
||||
Write-Host "$prompt (lascia vuoto per mantenere l'attuale)" -ForegroundColor Yellow
|
||||
$sec = Read-Host -AsSecureString
|
||||
if ($sec.Length -eq 0) { return $currentPlain }
|
||||
return [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($sec))
|
||||
}
|
||||
|
||||
# ---------- Editor guidato backup.conf ----------
|
||||
function Edit-BackupConfig {
|
||||
param([string]$cfgPath)
|
||||
|
||||
Write-Title "Modifica configurazione (backup.conf)"
|
||||
$doc = Load-Config $cfgPath
|
||||
|
||||
# Mappa variabili -> descrizione & validazione
|
||||
$vars = @(
|
||||
@{Key='BackupRoot'; Desc='Cartella radice dei backup (archivi, log, tool).'; Example='C:\Backups_AdHoc' },
|
||||
@{Key='LocalRetentionDaysFiles'; Desc='Giorni di retention dei file in locale (\Files). 0=disabilita.'; Validate='int' },
|
||||
@{Key='LocalRetentionDaysDb'; Desc='Giorni di retention dei database in locale (\Databases). 0=disabilita.'; Validate='int' },
|
||||
@{Key='RemoteRetentionDays'; Desc='Giorni di retention su remoto (rclone).'; Validate='int' },
|
||||
@{Key='KeepLocalArchives'; Desc='true=mantiene copie locali, false=le rimuove dopo upload.'; Validate='bool' },
|
||||
@{Key='EnableFileBackup'; Desc='true=abilita backup di cartelle/sorgenti in .7z.'; Validate='bool' },
|
||||
@{Key='EnableRcloneUpload'; Desc='true=abilita upload dei backup con rclone.'; Validate='bool' },
|
||||
@{Key='ArchiveSources'; Desc='Sorgenti da archiviare, separa con | (es.: C:\Dati|D:\Export|\\nas\share).'; Example='C:\Zucchetti\ahr90|C:\Zucchetti\NetSetup' },
|
||||
|
||||
@{Key='EnableSqlBackup'; Desc='true=abilita backup dei DB SQL Server.'; Validate='bool' },
|
||||
@{Key='SqlInstance'; Desc='Istanza SQL (es.: localhost, .\SQLEXPRESS, 192.168.1.10,1433, nome\istanza).'},
|
||||
@{Key='SqlUseWindowsAuth'; Desc='true=Windows Authentication; false=SQL Auth (usa SqlUser/SqlPassword).'; Validate='bool' },
|
||||
@{Key='SqlUser'; Desc='Utente SQL (usato solo se SqlUseWindowsAuth=false).'},
|
||||
@{Key='SqlPassword'; Desc='Password SQL (usata solo se SqlUseWindowsAuth=false).'; Secret=$true },
|
||||
@{Key='DbInclude'; Desc='Elenco DB da includere (|). Vuoto = auto-detect dei DB utente online.'; Example='DBProduzione|DBCRM|DBContabilita' },
|
||||
@{Key='DbExclude'; Desc='DB da escludere se DbInclude è vuoto (default: master|model|msdb|tempdb).', Example='master|model|msdb|tempdb' },
|
||||
@{Key='SqlCompressStage'; Desc='true= comprime la cartella _sql_stage in .7z dopo il backup.'; Validate='bool' },
|
||||
@{Key='SqlDropBakAfterZip'; Desc='true= elimina i .bak dopo la compressione.'; Validate='bool' },
|
||||
|
||||
@{Key='SevenZipCompressionLevel'; Desc='Livello compressione 7-Zip (0..9). 1–3 spesso è il miglior compromesso.'; Validate='int' },
|
||||
|
||||
@{Key='RcloneRemoteDest'; Desc='Destinazione rclone in formato REMOTE:percorso (usa %COMPUTERNAME% se utile).'; Example='dropbox:/Backups_AdHoc/%COMPUTERNAME%' },
|
||||
@{Key='RcloneBwl'; Desc='Limitazione di banda (es: 10M). Vuoto = nessun limite.'},
|
||||
@{Key='RcloneExtraArgs'; Desc='Argomenti extra rclone separati da | (es: --fast-list|--s3-chunk-size=64M).'},
|
||||
|
||||
@{Key='MailEnabled'; Desc='true= invia una mail di report a fine job.'; Validate='bool' },
|
||||
@{Key='MailSmtpHost'; Desc='Host SMTP (relay).'},
|
||||
@{Key='MailSmtpPort'; Desc='Porta SMTP (tipico 587).'; Validate='port' },
|
||||
@{Key='MailUseAuth'; Desc='true= il relay richiede autenticazione (compila utente/password).'; Validate='bool' },
|
||||
@{Key='MailUser'; Desc='Utente SMTP (se richiesto).'},
|
||||
@{Key='MailPassword'; Desc='Password SMTP (se richiesta).'; Secret=$true },
|
||||
@{Key='MailFrom'; Desc='Indirizzo mittente.'},
|
||||
@{Key='MailTo'; Desc='Destinatari separati da |.'; Example='it@azienda.it|sysadmin@azienda.it' },
|
||||
@{Key='MailSubjectPref'; Desc='Prefisso oggetto (puoi lasciare spazio finale).' }
|
||||
)
|
||||
|
||||
$changes = @{}
|
||||
|
||||
foreach ($v in $vars) {
|
||||
$key = $v.Key
|
||||
$cur = $doc.Map[$key]
|
||||
|
||||
Write-Host ""
|
||||
Write-Host ">> $key" -ForegroundColor Green
|
||||
Write-Host " $($v.Desc)"
|
||||
if ($v.ContainsKey('Example') -and $v.Example) { Write-Host " Esempio: $($v.Example)" -ForegroundColor DarkGray }
|
||||
|
||||
if ($v.ContainsKey('Secret') -and $v.Secret) {
|
||||
$newVal = Read-Secret "Nuovo valore per $key" $cur
|
||||
} else {
|
||||
$newValRaw = Read-Default "Inserisci un nuovo valore per $key (INVIO = mantieni)" $cur
|
||||
# Validazione
|
||||
if ($v.ContainsKey('Validate')) {
|
||||
switch ($v.Validate) {
|
||||
'int' { $n = Validate-IntNonNeg $newValRaw | Out-Null; if ($null -ne $n) { $newValRaw = [string]$n } }
|
||||
'bool' { $b = Normalize-BoolText $newValRaw ($cur -eq 'true'); $newValRaw = if ($b) { 'true' } else { 'false' } }
|
||||
'port' { $p = Validate-Port $newValRaw | Out-Null; if ($null -ne $p) { $newValRaw = [string]$p } }
|
||||
}
|
||||
}
|
||||
$newVal = $newValRaw
|
||||
}
|
||||
|
||||
if ($newVal -ne $cur) {
|
||||
$changes[$key] = @{Old=$cur; New=$newVal}
|
||||
Set-ConfigValue -doc $doc -key $key -value $newVal
|
||||
}
|
||||
}
|
||||
|
||||
if ($changes.Count -gt 0) {
|
||||
Write-Host ""
|
||||
Write-Title "Riepilogo modifiche"
|
||||
$changes.GetEnumerator() | ForEach-Object {
|
||||
"{0} : '{1}' -> '{2}'" -f $_.Key, $_.Value.Old, $_.Value.New
|
||||
} | Write-Host
|
||||
|
||||
if (Read-YesNo "Confermi il salvataggio delle modifiche?") {
|
||||
$bk = Save-Config -doc $doc -path $cfgPath
|
||||
Write-Host "Configurazione salvata: $cfgPath" -ForegroundColor Cyan
|
||||
Write-Host "Backup creato: $bk" -ForegroundColor DarkCyan
|
||||
} else {
|
||||
Write-Host "Annullato. Nessuna modifica scritta." -ForegroundColor Yellow
|
||||
}
|
||||
} else {
|
||||
Write-Host "Nessuna modifica apportata." -ForegroundColor Yellow
|
||||
}
|
||||
|
||||
Pause-Enter
|
||||
}
|
||||
|
||||
# ---------- Installazione ambiente ----------
|
||||
function Ensure-Folder($path) {
|
||||
if (-not (Test-Path -LiteralPath $path)) {
|
||||
New-Item -ItemType Directory -Path $path -Force | Out-Null
|
||||
}
|
||||
}
|
||||
|
||||
function Install-Environment {
|
||||
param([string]$cfgPath)
|
||||
|
||||
Write-Title "Installazione ambiente"
|
||||
$doc = Load-Config $cfgPath
|
||||
|
||||
# Determina BackupRoot
|
||||
$backupRoot = if ($BackupRootOverride) { $BackupRootOverride } elseif ($doc.Map['BackupRoot']) { $doc.Map['BackupRoot'] } else { 'C:\Backups_AdHoc' }
|
||||
$backupRoot = Read-Default "BackupRoot di destinazione" $backupRoot
|
||||
Set-ConfigValue -doc $doc -key 'BackupRoot' -value $backupRoot
|
||||
$null = Save-Config -doc $doc -path $cfgPath
|
||||
|
||||
# Struttura cartelle
|
||||
$folders = @(
|
||||
$backupRoot,
|
||||
Join-Path $backupRoot 'Logs',
|
||||
Join-Path $backupRoot 'Out',
|
||||
Join-Path $backupRoot 'Files',
|
||||
Join-Path $backupRoot 'Databases',
|
||||
Join-Path $backupRoot '_sql_stage',
|
||||
Join-Path $backupRoot 'Bin',
|
||||
Join-Path $backupRoot 'RClone'
|
||||
)
|
||||
foreach ($f in $folders) { Ensure-Folder $f }
|
||||
|
||||
# Copia script principali
|
||||
$srcScript = Join-Path $PSScriptRoot 'AdHoc-Backup.ps1'
|
||||
if (-not (Test-Path -LiteralPath $srcScript)) {
|
||||
throw "AdHoc-Backup.ps1 non trovato in $PSScriptRoot"
|
||||
}
|
||||
Copy-Item -LiteralPath $srcScript -Destination (Join-Path $backupRoot 'AdHoc-Backup.ps1') -Force
|
||||
|
||||
# Copia backup.conf
|
||||
Copy-Item -LiteralPath $cfgPath -Destination (Join-Path $backupRoot 'backup.conf') -Force
|
||||
|
||||
# Rclone: binario + config locale nello stesso folder ($BackupRoot\RClone)
|
||||
Ensure-Rclone -TargetRoot $backupRoot
|
||||
|
||||
Write-Host "Installazione completata in: $backupRoot" -ForegroundColor Cyan
|
||||
Pause-Enter
|
||||
}
|
||||
|
||||
function Ensure-Rclone {
|
||||
param([string]$TargetRoot)
|
||||
|
||||
$rcloneDir = Join-Path $TargetRoot 'RClone'
|
||||
$rcloneExe = Join-Path $rcloneDir 'rclone.exe'
|
||||
$rcloneConf = Join-Path $rcloneDir 'rclone.conf'
|
||||
|
||||
Ensure-Folder $rcloneDir
|
||||
|
||||
if (-not (Test-Path -LiteralPath $rcloneExe)) {
|
||||
Write-Host "rclone.exe non trovato in $rcloneDir, provo a reperirlo..." -ForegroundColor Yellow
|
||||
$candidates = @(
|
||||
"$env:ProgramFiles\rclone\rclone.exe",
|
||||
"$env:ProgramFiles\Rclone\rclone.exe",
|
||||
"$env:ProgramFiles(x86)\rclone\rclone.exe",
|
||||
"$env:ProgramFiles(x86)\Rclone\rclone.exe",
|
||||
"$env:SystemRoot\System32\rclone.exe"
|
||||
) | Where-Object { Test-Path -LiteralPath $_ }
|
||||
|
||||
if ($candidates.Count -gt 0) {
|
||||
Copy-Item -LiteralPath $candidates[0] -Destination $rcloneExe -Force
|
||||
Write-Host "Copiato rclone da: $($candidates[0])" -ForegroundColor Green
|
||||
}
|
||||
else {
|
||||
# Tentativo di download (puoi saltarlo se non vuoi traffico Internet)
|
||||
try {
|
||||
$zipUrl = "https://downloads.rclone.org/rclone-current-windows-amd64.zip"
|
||||
$tmpZip = Join-Path $env:TEMP "rclone-current.zip"
|
||||
Write-Host "Scarico rclone da $zipUrl ..." -ForegroundColor Yellow
|
||||
Invoke-WebRequest -Uri $zipUrl -OutFile $tmpZip -UseBasicParsing
|
||||
$tmpDir = Join-Path $env:TEMP "rclone_unzip_$([guid]::NewGuid().ToString('N'))"
|
||||
Expand-Archive -LiteralPath $tmpZip -DestinationPath $tmpDir -Force
|
||||
$found = Get-ChildItem -Path $tmpDir -Filter rclone.exe -Recurse | Select-Object -First 1
|
||||
if ($null -ne $found) {
|
||||
Copy-Item -LiteralPath $found.FullName -Destination $rcloneExe -Force
|
||||
Write-Host "rclone scaricato e installato." -ForegroundColor Green
|
||||
} else {
|
||||
Write-Host "Impossibile trovare rclone.exe nello ZIP. Mettilo manualmente in $rcloneDir" -ForegroundColor Red
|
||||
}
|
||||
} catch {
|
||||
Write-Host "Download rclone fallito: $($_.Exception.Message)" -ForegroundColor Red
|
||||
Write-Host "Puoi copiare manualmente rclone.exe in $rcloneDir" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Config rclone: importa quella utente se esiste, altrimenti crea skeleton
|
||||
if (-not (Test-Path -LiteralPath $rcloneConf)) {
|
||||
$userConf = Join-Path $env:APPDATA 'rclone\rclone.conf'
|
||||
if (Test-Path -LiteralPath $userConf) {
|
||||
Copy-Item -LiteralPath $userConf -Destination $rcloneConf -Force
|
||||
Write-Host "Importata configurazione rclone da $userConf" -ForegroundColor Green
|
||||
} else {
|
||||
@"
|
||||
# rclone.conf (skeleton) — definisci qui il REMOTE usato in backup.conf (RcloneRemoteDest)
|
||||
# Esempio dropbox:
|
||||
# [dropbox]
|
||||
# type = dropbox
|
||||
"@ | Set-Content -LiteralPath $rcloneConf -Encoding UTF8 -NoNewline
|
||||
Write-Host "Creato skeleton rclone.conf in $rcloneConf" -ForegroundColor Yellow
|
||||
}
|
||||
}
|
||||
|
||||
# NB: il nostro backup script userà --config "$BackupRoot\RClone\rclone.conf" come da linea guida.
|
||||
}
|
||||
|
||||
# ---------- Pianificazione ----------
|
||||
function Create-Schedule {
|
||||
param([string]$cfgPath)
|
||||
|
||||
if (-not (Test-Admin)) {
|
||||
throw "Per creare la pianificazione occorrono privilegi di Amministratore."
|
||||
}
|
||||
|
||||
$doc = Load-Config $cfgPath
|
||||
$backupRoot = if ($BackupRootOverride) { $BackupRootOverride } elseif ($doc.Map['BackupRoot']) { $doc.Map['BackupRoot'] } else { 'C:\Backups_AdHoc' }
|
||||
|
||||
$scriptPath = Join-Path $backupRoot 'AdHoc-Backup.ps1'
|
||||
$confPath = Join-Path $backupRoot 'backup.conf'
|
||||
if (-not (Test-Path -LiteralPath $scriptPath)) { throw "Script backup non trovato in $scriptPath (esegui prima l'installazione)." }
|
||||
if (-not (Test-Path -LiteralPath $confPath)) { throw "Configurazione non trovata in $confPath (esegui prima l'installazione)." }
|
||||
|
||||
Write-Title "Crea pianificazione (Scheduled Task)"
|
||||
$taskName = Read-Default "Nome attività" "AdHoc Backup Giornaliero"
|
||||
|
||||
# Ora (HH:mm)
|
||||
while ($true) {
|
||||
$timeTxt = Read-Default "Orario giornaliero (HH:mm)" "22:30"
|
||||
if ([TimeSpan]::TryParse($timeTxt, [ref]([TimeSpan]$null))) { break }
|
||||
try { [DateTime]::ParseExact($timeTxt, 'HH:mm', $null) | Out-Null; break } catch { Write-Host "Formato non valido. Usa HH:mm (es. 22:30)" -ForegroundColor Yellow }
|
||||
}
|
||||
|
||||
$atTime = [DateTime]::ParseExact($timeTxt, 'HH:mm', $null).TimeOfDay
|
||||
$trigger = New-ScheduledTaskTrigger -Daily -At $atTime
|
||||
|
||||
$psArgs = "-NoProfile -ExecutionPolicy Bypass -File `"$scriptPath`" -Conf `"$confPath`""
|
||||
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument $psArgs
|
||||
|
||||
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
|
||||
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable `
|
||||
-RestartCount 3 -RestartInterval (New-TimeSpan -Minutes 5) `
|
||||
-MultipleInstances IgnoreNew
|
||||
|
||||
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
|
||||
|
||||
Write-Host "Attività pianificata creata: $taskName, orario $timeTxt (account SYSTEM)." -ForegroundColor Cyan
|
||||
Pause-Enter
|
||||
}
|
||||
|
||||
# ---------- Menù ----------
|
||||
function Show-Menu {
|
||||
Clear-Host
|
||||
Write-Host ""
|
||||
Write-Host "==========================" -ForegroundColor DarkCyan
|
||||
Write-Host " INSTALL AZIENDALE BACKUP" -ForegroundColor DarkCyan
|
||||
Write-Host "==========================" -ForegroundColor DarkCyan
|
||||
Write-Host "1) Installa lo script e l'ambiente"
|
||||
Write-Host "2) Crea pianificazione (Scheduled Task)"
|
||||
Write-Host "3) Modifica configurazione (backup.conf)"
|
||||
Write-Host "4) Esci"
|
||||
Write-Host ""
|
||||
}
|
||||
|
||||
# Percorso di default per backup.conf
|
||||
if (-not $ConfigPath) { $ConfigPath = Join-Path $PSScriptRoot 'backup.conf' }
|
||||
|
||||
while ($true) {
|
||||
Show-Menu
|
||||
$choice = Read-Default "Seleziona un'opzione" "1"
|
||||
switch ($choice) {
|
||||
'1' {
|
||||
Install-Environment -cfgPath $ConfigPath
|
||||
}
|
||||
'2' {
|
||||
Create-Schedule -cfgPath $ConfigPath
|
||||
}
|
||||
'3' {
|
||||
Edit-BackupConfig -cfgPath $ConfigPath
|
||||
}
|
||||
'4' { break }
|
||||
default {
|
||||
Write-Host "Opzione non valida." -ForegroundColor Yellow
|
||||
Pause-Enter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "Uscita." -ForegroundColor DarkGray
|
||||
|
||||
Loading…
Reference in New Issue
Block a user