Compare commits
2 Commits
1ac04e0b79
...
d964631532
| Author | SHA1 | Date | |
|---|---|---|---|
| d964631532 | |||
| 9d0e2f7fde |
37
backup.ps1
37
backup.ps1
@@ -1,14 +1,19 @@
|
|||||||
|
param(
|
||||||
|
[Parameter(Mandatory=$true)]
|
||||||
|
[string]$SqlInstance
|
||||||
|
)
|
||||||
|
|
||||||
#
|
#
|
||||||
# backup.ps1
|
# backup.ps1
|
||||||
#
|
#
|
||||||
# TODO: Update cleanup time based on backup type
|
# TODO: Update cleanup time based on backup type
|
||||||
|
|
||||||
$sqlInstance = "sqlfcsql\TESTINST"
|
$instanceName = $SqlInstance.Split('\')[1]
|
||||||
#$directory = "H:\Backup"
|
|
||||||
$directory = "C:\Rubrik\mount"
|
$directory = "C:\Rubrik\$instanceName"
|
||||||
$fullBackupDay = 'Thursday'
|
$fullBackupDay = 'Thursday'
|
||||||
$checkCluster = $false
|
$checkCluster = $false
|
||||||
$logFile = "C:\Rubrik\backup.log"
|
$logFile = "C:\Rubrik\backup-$instanceName.log"
|
||||||
|
|
||||||
$fullFlag = $directory + "\last_full.flag"
|
$fullFlag = $directory + "\last_full.flag"
|
||||||
$diffFlag = $directory + "\last_diff.flag"
|
$diffFlag = $directory + "\last_diff.flag"
|
||||||
@@ -29,21 +34,35 @@ function Write-Log($message) {
|
|||||||
Write-Host $logEntry
|
Write-Host $logEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Check if directory exists and is a symbolic link
|
||||||
|
if (-not (Test-Path $directory)) {
|
||||||
|
Write-Log "ERROR: Directory '$directory' does not exist. Exiting script."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$directoryInfo = Get-Item $directory
|
||||||
|
if (-not ($directoryInfo.Attributes -band [System.IO.FileAttributes]::ReparsePoint)) {
|
||||||
|
Write-Log "ERROR: Directory '$directory' is not a symbolic link. Exiting script."
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Log "INFO: Directory '$directory' exists and is a symbolic link. Target: $($directoryInfo.Target). Proceeding."
|
||||||
|
|
||||||
if ($checkCluster) {
|
if ($checkCluster) {
|
||||||
# Check if SQL instance is running locally
|
# Check if SQL instance is running locally
|
||||||
$localNode = $env:COMPUTERNAME
|
$localNode = $env:COMPUTERNAME
|
||||||
$instanceName = $sqlInstance.Split('\')[1]
|
|
||||||
$clusterInstance = Get-ClusterResource | Where-Object { $_.ResourceType -eq "SQL Server" -and $_.Name -eq "SQL Server ($instanceName)" }
|
$clusterInstance = Get-ClusterResource | Where-Object { $_.ResourceType -eq "SQL Server" -and $_.Name -eq "SQL Server ($instanceName)" }
|
||||||
if ($clusterInstance) {
|
if ($clusterInstance) {
|
||||||
$ownerNode = $clusterInstance.OwnerNode.Name
|
$ownerNode = $clusterInstance.OwnerNode.Name
|
||||||
if ($ownerNode -ne $localNode) {
|
if ($ownerNode -ne $localNode) {
|
||||||
Write-Log "SQL instance '$sqlInstance' is not running on local node '$localNode'. Exiting script."
|
Write-Log "SQL instance '$SqlInstance' is not running on local node '$localNode'. Exiting script."
|
||||||
exit 1
|
exit 1
|
||||||
} else {
|
} else {
|
||||||
Write-Log "SQL instance '$sqlInstance' is running on local node '$localNode'. Proceeding."
|
Write-Log "SQL instance '$SqlInstance' is running on local node '$localNode'. Proceeding."
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Write-Log "ERROR: SQL instance '$sqlInstance' not found in cluster resources."
|
Write-Log "ERROR: SQL instance '$SqlInstance' not found in cluster resources."
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -77,7 +96,7 @@ if ((Get-Date).DayOfWeek -eq $fullBackupDay) {
|
|||||||
$query = "EXECUTE [dbo].[DatabaseBackup] @Databases = 'ALL_DATABASES', @Directory = '$directory', @BackupType = '$backupType', @Verify = 'N', @CleanupTime = $cleanupTime, @CheckSum = 'Y', @LogToTable = 'Y'"
|
$query = "EXECUTE [dbo].[DatabaseBackup] @Databases = 'ALL_DATABASES', @Directory = '$directory', @BackupType = '$backupType', @Verify = 'N', @CleanupTime = $cleanupTime, @CheckSum = 'Y', @LogToTable = 'Y'"
|
||||||
Write-Log "Executing backup type: $backupType"
|
Write-Log "Executing backup type: $backupType"
|
||||||
|
|
||||||
$sqlcmdOutput = & sqlcmd -S $sqlInstance -Q $query 2>&1
|
$sqlcmdOutput = & sqlcmd -S $SqlInstance -Q $query 2>&1
|
||||||
$sqlcmdExitCode = $LASTEXITCODE
|
$sqlcmdExitCode = $LASTEXITCODE
|
||||||
|
|
||||||
if ($sqlcmdExitCode -eq 0) {
|
if ($sqlcmdExitCode -eq 0) {
|
||||||
|
|||||||
@@ -76,12 +76,16 @@ try {
|
|||||||
$logFile = Join-Path $tempDir "Create-SA-File.log"
|
$logFile = Join-Path $tempDir "Create-SA-File.log"
|
||||||
|
|
||||||
# ---- Create the one-shot script that will run under the service account ----
|
# ---- Create the one-shot script that will run under the service account ----
|
||||||
|
$escapedSaJsonPath = $SaJsonPath -replace '\\', '\\'
|
||||||
|
$escapedOutputXmlPath = $OutputXmlPath -replace '\\', '\\'
|
||||||
|
$escapedLogFile = $logFile -replace '\\', '\\'
|
||||||
|
|
||||||
$oneShotContent = @"
|
$oneShotContent = @"
|
||||||
# One-shot script created by create-and-run-one-shot-via-gMSA.ps1
|
# One-shot script created by create-and-run-one-shot-via-gMSA.ps1
|
||||||
# Runs RubrikSecurityCloud command to create service-account file
|
# Runs RubrikSecurityCloud command to create service-account file
|
||||||
|
|
||||||
# Start transcript for detailed logging
|
# Start transcript for detailed logging
|
||||||
Start-Transcript -Path `"$logFile`" -Append
|
Start-Transcript -Path "$escapedLogFile" -Append
|
||||||
|
|
||||||
Write-Output "Script started at: `$(Get-Date)"
|
Write-Output "Script started at: `$(Get-Date)"
|
||||||
Write-Output "Running as user: `$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
|
Write-Output "Running as user: `$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)"
|
||||||
@@ -99,36 +103,36 @@ Try {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Try {
|
Try {
|
||||||
Write-Output "Checking input file: $SaJsonPath"
|
Write-Output "Checking input file: $escapedSaJsonPath"
|
||||||
# Ensure the input file exists
|
# Ensure the input file exists
|
||||||
if (-not (Test-Path -Path `"$SaJsonPath`")) {
|
if (-not (Test-Path -Path "$escapedSaJsonPath")) {
|
||||||
Write-Error "Input SA JSON not found: $SaJsonPath"
|
Write-Error "Input SA JSON not found: $escapedSaJsonPath"
|
||||||
Stop-Transcript
|
Stop-Transcript
|
||||||
Exit 3
|
Exit 3
|
||||||
}
|
}
|
||||||
Write-Output "Input file found, size: `$((Get-Item `"$SaJsonPath`").Length) bytes"
|
Write-Output "Input file found, size: `$((Get-Item "$escapedSaJsonPath").Length) bytes"
|
||||||
|
|
||||||
Write-Output "Calling Set-RscServiceAccountFile..."
|
Write-Output "Calling Set-RscServiceAccountFile..."
|
||||||
Write-Output " Input: $SaJsonPath"
|
Write-Output " Input: $escapedSaJsonPath"
|
||||||
Write-Output " Output: $OutputXmlPath"
|
Write-Output " Output: $escapedOutputXmlPath"
|
||||||
|
|
||||||
Set-RscServiceAccountFile `"$SaJsonPath`" -OutputFilePath `"$OutputXmlPath`" -Verbose
|
Set-RscServiceAccountFile "$escapedSaJsonPath" -OutputFilePath "$escapedOutputXmlPath" -Verbose
|
||||||
|
|
||||||
Write-Output "Set-RscServiceAccountFile completed"
|
Write-Output "Set-RscServiceAccountFile completed"
|
||||||
|
|
||||||
if (Test-Path -Path `"$OutputXmlPath`") {
|
if (Test-Path -Path "$escapedOutputXmlPath") {
|
||||||
Write-Output "Service account XML created successfully: $OutputXmlPath"
|
Write-Output "Service account XML created successfully: $escapedOutputXmlPath"
|
||||||
Write-Output "Output file size: `$((Get-Item `"$OutputXmlPath`").Length) bytes"
|
Write-Output "Output file size: `$((Get-Item "$escapedOutputXmlPath").Length) bytes"
|
||||||
Stop-Transcript
|
Stop-Transcript
|
||||||
Exit 0
|
Exit 0
|
||||||
} else {
|
} else {
|
||||||
Write-Error "Set-RscServiceAccountFile completed but output file not found: $OutputXmlPath"
|
Write-Error "Set-RscServiceAccountFile completed but output file not found: $escapedOutputXmlPath"
|
||||||
Write-Error "Checking parent directory: `$(Split-Path `"$OutputXmlPath`")"
|
Write-Error "Checking parent directory: `$(Split-Path "$escapedOutputXmlPath")"
|
||||||
if (Test-Path (Split-Path `"$OutputXmlPath`")) {
|
if (Test-Path (Split-Path "$escapedOutputXmlPath")) {
|
||||||
Write-Output "Parent directory exists, listing contents:"
|
Write-Output "Parent directory exists, listing contents:"
|
||||||
Get-ChildItem (Split-Path `"$OutputXmlPath`") | ForEach-Object { Write-Output " `$(`$_.Name)" }
|
Get-ChildItem (Split-Path "$escapedOutputXmlPath") | ForEach-Object { Write-Output " `$(`$_.Name)" }
|
||||||
} else {
|
} else {
|
||||||
Write-Error "Parent directory does not exist: `$(Split-Path `"$OutputXmlPath`")"
|
Write-Error "Parent directory does not exist: `$(Split-Path "$escapedOutputXmlPath")"
|
||||||
}
|
}
|
||||||
Stop-Transcript
|
Stop-Transcript
|
||||||
Exit 4
|
Exit 4
|
||||||
@@ -142,29 +146,37 @@ Try {
|
|||||||
}
|
}
|
||||||
"@
|
"@
|
||||||
|
|
||||||
# Replace placeholders (so we don't have to escape too much)
|
|
||||||
$oneShotContent = $oneShotContent -replace '\$SaJsonPath', [Regex]::Escape($SaJsonPath)
|
|
||||||
$oneShotContent = $oneShotContent -replace '\$OutputXmlPath', [Regex]::Escape($OutputXmlPath)
|
|
||||||
$oneShotContent = $oneShotContent -replace '\$logFile', [Regex]::Escape($logFile)
|
|
||||||
|
|
||||||
Set-Content -Path $oneShotScript -Value $oneShotContent -Encoding UTF8
|
Set-Content -Path $oneShotScript -Value $oneShotContent -Encoding UTF8
|
||||||
|
|
||||||
# Make sure executable by scheduled task
|
# Make sure executable by scheduled task
|
||||||
|
try {
|
||||||
icacls $oneShotScript /grant "BUILTIN\Administrators:(R,W)" | Out-Null
|
icacls $oneShotScript /grant "BUILTIN\Administrators:(R,W)" | Out-Null
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Could not set permissions on script file: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
# ---- Build Scheduled Task objects ----
|
# ---- Build Scheduled Task objects ----
|
||||||
# Construct the UserId based on account type
|
# Construct the UserId based on account type
|
||||||
if ($AccountType -eq 'gMSA') {
|
if ($AccountType -eq 'gMSA') {
|
||||||
$userId = if ([string]::IsNullOrWhiteSpace($Domain)) { "$AccountName`$" } else { "$Domain\$AccountName`$" }
|
if ([string]::IsNullOrWhiteSpace($Domain)) {
|
||||||
|
$userId = "$AccountName`$"
|
||||||
|
} else {
|
||||||
|
$userId = "$Domain\$AccountName`$"
|
||||||
|
}
|
||||||
$logonType = 'Password' # For gMSA, use Password logon type
|
$logonType = 'Password' # For gMSA, use Password logon type
|
||||||
} else {
|
} else {
|
||||||
$userId = if ([string]::IsNullOrWhiteSpace($Domain)) { $AccountName } else { "$Domain\$AccountName" }
|
if ([string]::IsNullOrWhiteSpace($Domain)) {
|
||||||
|
$userId = $AccountName
|
||||||
|
} else {
|
||||||
|
$userId = "$Domain\$AccountName"
|
||||||
|
}
|
||||||
$logonType = 'Password' # For regular service accounts, use Password logon type
|
$logonType = 'Password' # For regular service accounts, use Password logon type
|
||||||
}
|
}
|
||||||
|
|
||||||
# Action: run PowerShell to execute the one-shot script with output redirection
|
# Action: run PowerShell to execute the one-shot script with output redirection
|
||||||
$psArgs = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File `"$oneShotScript`" *>&1 | Tee-Object -FilePath `"$logFile`" -Append"
|
$psExePath = Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe'
|
||||||
$action = New-ScheduledTaskAction -Execute (Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe') -Argument $psArgs
|
$psArgs = "-NoProfile -NonInteractive -ExecutionPolicy Bypass -File `"$oneShotScript`""
|
||||||
|
$action = New-ScheduledTaskAction -Execute $psExePath -Argument $psArgs
|
||||||
|
|
||||||
# Trigger: once, a short time in the future (1 minute from now)
|
# Trigger: once, a short time in the future (1 minute from now)
|
||||||
$startTime = (Get-Date).AddMinutes(1)
|
$startTime = (Get-Date).AddMinutes(1)
|
||||||
@@ -185,8 +197,13 @@ Try {
|
|||||||
Write-Host "Registered scheduled task '$TaskName' to run as gMSA $userId at $startTime."
|
Write-Host "Registered scheduled task '$TaskName' to run as gMSA $userId at $startTime."
|
||||||
} else {
|
} else {
|
||||||
# For regular service accounts, register with password
|
# For regular service accounts, register with password
|
||||||
$securePassword = if ($Password -is [SecureString]) { $Password } else { ConvertTo-SecureString $Password -AsPlainText -Force }
|
if ($Password -is [SecureString]) {
|
||||||
Register-ScheduledTask -TaskName $TaskName -InputObject $task -User $userId -Password ([Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword))) -Force
|
$securePassword = $Password
|
||||||
|
} else {
|
||||||
|
$securePassword = ConvertTo-SecureString $Password -AsPlainText -Force
|
||||||
|
}
|
||||||
|
$plainPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword))
|
||||||
|
Register-ScheduledTask -TaskName $TaskName -InputObject $task -User $userId -Password $plainPassword -Force
|
||||||
Write-Host "Registered scheduled task '$TaskName' to run as service account $userId at $startTime."
|
Write-Host "Registered scheduled task '$TaskName' to run as service account $userId at $startTime."
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -205,6 +222,7 @@ Try {
|
|||||||
Start-Sleep -Seconds $pollInterval
|
Start-Sleep -Seconds $pollInterval
|
||||||
$elapsed += $pollInterval
|
$elapsed += $pollInterval
|
||||||
|
|
||||||
|
try {
|
||||||
$info = Get-ScheduledTaskInfo -TaskName $TaskName -ErrorAction SilentlyContinue
|
$info = Get-ScheduledTaskInfo -TaskName $TaskName -ErrorAction SilentlyContinue
|
||||||
if ($null -eq $info) {
|
if ($null -eq $info) {
|
||||||
Write-Warning "Could not query task info yet."
|
Write-Warning "Could not query task info yet."
|
||||||
@@ -218,7 +236,7 @@ Try {
|
|||||||
# 1. State is Ready/Disabled/Unknown AND we have a valid LastResult
|
# 1. State is Ready/Disabled/Unknown AND we have a valid LastResult
|
||||||
# 2. OR if LastResult changed from 267009 (SCHED_S_TASK_RUNNING) to something else
|
# 2. OR if LastResult changed from 267009 (SCHED_S_TASK_RUNNING) to something else
|
||||||
if (($state -eq 'Ready' -or $state -eq 'Disabled' -or $state -eq 'Unknown' -or [string]::IsNullOrEmpty($state)) -and
|
if (($state -eq 'Ready' -or $state -eq 'Disabled' -or $state -eq 'Unknown' -or [string]::IsNullOrEmpty($state)) -and
|
||||||
($lastResult -ne $null -and $lastResult -ne 267009)) {
|
($null -ne $lastResult -and $lastResult -ne 267009)) {
|
||||||
$taskCompleted = $true
|
$taskCompleted = $true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -227,6 +245,9 @@ Try {
|
|||||||
Write-Host "Task still running..."
|
Write-Host "Task still running..."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Warning "Error querying task status: $($_.Exception.Message)"
|
||||||
|
}
|
||||||
|
|
||||||
if ($elapsed -ge $maxWaitSeconds) {
|
if ($elapsed -ge $maxWaitSeconds) {
|
||||||
throw "Timed out waiting for scheduled task to finish (waited $maxWaitSeconds seconds)."
|
throw "Timed out waiting for scheduled task to finish (waited $maxWaitSeconds seconds)."
|
||||||
@@ -270,5 +291,11 @@ Try {
|
|||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
Write-Error "ERROR: $($_.Exception.Message)"
|
Write-Error "ERROR: $($_.Exception.Message)"
|
||||||
|
if (-not $KeepArtifacts -and $TaskName) {
|
||||||
|
try { Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue } catch {}
|
||||||
|
}
|
||||||
|
if (-not $KeepArtifacts -and $tempDir -and (Test-Path $tempDir)) {
|
||||||
|
try { Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue } catch {}
|
||||||
|
}
|
||||||
throw
|
throw
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user