151 lines
4.6 KiB
PowerShell
151 lines
4.6 KiB
PowerShell
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidateSet("Notifier", "BackupOnLogoff")]
|
|
[string]$Task,
|
|
|
|
[string]$TaskFolder = "BakRest",
|
|
|
|
[switch]$RunAsCurrentUser,
|
|
|
|
[switch]$RunAsStoredCredentials,
|
|
|
|
[string]$UserName,
|
|
|
|
[switch]$NoShutdown
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$repoRoot = Split-Path -Parent $PSScriptRoot
|
|
$pythonExe = (& python -c "import sys; print(sys.executable)").Trim()
|
|
|
|
function Resolve-TaskUserName {
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$Name
|
|
)
|
|
|
|
if ($Name.StartsWith(".\")) {
|
|
return "$env:COMPUTERNAME\$($Name.Substring(2))"
|
|
}
|
|
|
|
return $Name
|
|
}
|
|
|
|
if (-not $pythonExe -or -not (Test-Path -LiteralPath $pythonExe)) {
|
|
$pythonCommand = Get-Command python -ErrorAction Stop
|
|
$pythonExe = $pythonCommand.Source
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $pythonExe)) {
|
|
throw "Python executable not found. Run this script from the same shell where 'python' works."
|
|
}
|
|
|
|
if ($Task -eq "Notifier") {
|
|
$taskName = "$TaskFolder\ServerNotifierEvery30Minutes"
|
|
$xmlPath = Join-Path $repoRoot "tasks\BakRestServerNotifierEvery30Minutes.xml"
|
|
} else {
|
|
$taskName = "$TaskFolder\BackupOnLogoff"
|
|
$xmlPath = Join-Path $repoRoot "tasks\BakRestBackupOnLogoff.xml"
|
|
}
|
|
|
|
if (-not (Test-Path -LiteralPath $xmlPath)) {
|
|
throw "Task XML not found: $xmlPath"
|
|
}
|
|
|
|
$xml = Get-Content -LiteralPath $xmlPath -Raw
|
|
$xml = $xml -replace 'encoding="UTF-8"', 'encoding="UTF-16"'
|
|
$xml = $xml -replace '<Command>.*?</Command>', ('<Command>{0}</Command>' -f [System.Security.SecurityElement]::Escape($pythonExe))
|
|
$xml = $xml -replace '<WorkingDirectory>.*?</WorkingDirectory>', ('<WorkingDirectory>{0}</WorkingDirectory>' -f [System.Security.SecurityElement]::Escape($repoRoot))
|
|
|
|
if ($NoShutdown -and $Task -eq "BackupOnLogoff") {
|
|
$xml = $xml -replace '<Arguments>-m bakrest\.tray_app --nogui</Arguments>', '<Arguments>-m bakrest.tray_app --nogui --no-shutdown</Arguments>'
|
|
}
|
|
|
|
if ($RunAsStoredCredentials -and $RunAsCurrentUser) {
|
|
throw "Use either -RunAsCurrentUser or -RunAsStoredCredentials, not both."
|
|
}
|
|
|
|
if ($RunAsCurrentUser) {
|
|
$currentIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
|
|
$currentSid = $currentIdentity.User.Value
|
|
$principalXml = @"
|
|
<Principals>
|
|
<Principal id="Author">
|
|
<UserId>$currentSid</UserId>
|
|
<LogonType>InteractiveToken</LogonType>
|
|
<RunLevel>LeastPrivilege</RunLevel>
|
|
</Principal>
|
|
</Principals>
|
|
"@
|
|
$xml = [regex]::Replace(
|
|
$xml,
|
|
'<Principals>.*?</Principals>',
|
|
$principalXml,
|
|
[System.Text.RegularExpressions.RegexOptions]::Singleline
|
|
)
|
|
}
|
|
|
|
$plainPassword = $null
|
|
$runAsName = $null
|
|
if ($RunAsStoredCredentials) {
|
|
if (-not $UserName) {
|
|
$UserName = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name
|
|
}
|
|
$UserName = Resolve-TaskUserName -Name $UserName
|
|
|
|
$securePassword = Read-Host -Prompt "Password for $UserName" -AsSecureString
|
|
$passwordPtr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword)
|
|
try {
|
|
$plainPassword = [Runtime.InteropServices.Marshal]::PtrToStringBSTR($passwordPtr)
|
|
} finally {
|
|
[Runtime.InteropServices.Marshal]::ZeroFreeBSTR($passwordPtr)
|
|
}
|
|
|
|
$principalXml = @"
|
|
<Principals>
|
|
<Principal id="Author">
|
|
<UserId>$([System.Security.SecurityElement]::Escape($UserName))</UserId>
|
|
<LogonType>Password</LogonType>
|
|
<RunLevel>LeastPrivilege</RunLevel>
|
|
</Principal>
|
|
</Principals>
|
|
"@
|
|
$xml = [regex]::Replace(
|
|
$xml,
|
|
'<Principals>.*?</Principals>',
|
|
$principalXml,
|
|
[System.Text.RegularExpressions.RegexOptions]::Singleline
|
|
)
|
|
$runAsName = $UserName
|
|
}
|
|
|
|
$tempXml = Join-Path $env:TEMP ("bakrest-task-{0}.xml" -f ([Guid]::NewGuid()))
|
|
[System.IO.File]::WriteAllText($tempXml, $xml, [System.Text.Encoding]::Unicode)
|
|
|
|
try {
|
|
if ($RunAsStoredCredentials) {
|
|
schtasks.exe /Create /TN $taskName /XML $tempXml /RU $UserName /RP $plainPassword /F
|
|
} else {
|
|
schtasks.exe /Create /TN $taskName /XML $tempXml /F
|
|
}
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "schtasks failed with exit code $LASTEXITCODE"
|
|
}
|
|
Write-Host "Registered task: $taskName"
|
|
Write-Host "Python: $pythonExe"
|
|
Write-Host "WorkingDirectory: $repoRoot"
|
|
if ($RunAsCurrentUser) {
|
|
Write-Host "RunAs: $($currentIdentity.Name)"
|
|
}
|
|
if ($RunAsStoredCredentials) {
|
|
Write-Host "RunAs: $runAsName (stored credentials)"
|
|
}
|
|
if ($NoShutdown -and $Task -eq "BackupOnLogoff") {
|
|
Write-Host "BackupOnLogoff registered in debug mode: --no-shutdown"
|
|
}
|
|
} finally {
|
|
$plainPassword = $null
|
|
Remove-Item -LiteralPath $tempXml -Force -ErrorAction SilentlyContinue
|
|
}
|