diff --git a/backup.ps1 b/backup.ps1 index 17e37a3..3795f01 100644 --- a/backup.ps1 +++ b/backup.ps1 @@ -6,13 +6,13 @@ $sqlInstance = "sqlfcsql\TESTINST" #$directory = "H:\Backup" $directory = "C:\Rubrik\mount" -$fullBackupDay = 'Tuesday' +$fullBackupDay = 'Thursday' $checkCluster = $false +$logFile = "C:\Rubrik\backup.log" $fullFlag = $directory + "\last_full.flag" $diffFlag = $directory + "\last_diff.flag" $today = (Get-Date).Date -$logFile = "C:\Rubrik\backup.log" function FlagTakenToday($flagPath) { if (Test-Path $flagPath) { @@ -58,6 +58,7 @@ if ((Get-Date).DayOfWeek -eq $fullBackupDay) { Write-Log "Selected FULL backup. Flag updated." } else { $backupType = "LOG" + $cleanupTime = 24 Write-Log "FULL backup already taken today. Selected LOG backup." } } else { diff --git a/createSAcreds.ps1 b/createSAcreds.ps1 new file mode 100644 index 0000000..a433536 --- /dev/null +++ b/createSAcreds.ps1 @@ -0,0 +1,274 @@ +<# +.SYNOPSIS + Create a one-shot scheduled task that runs as a gMSA or service account to create a Rubrik service-account file. + +.DESCRIPTION + - Creates a temporary PowerShell script that calls Set-RscServiceAccountFile with given parameters. + - Registers a scheduled task whose principal is either a gMSA or regular service account. + - Starts the task, waits for completion, checks LastTaskResult, then optionally cleans up. + +.PARAMETER Domain + The AD domain (e.g. AD). If already providing fully-qualified account, set to empty string. + +.PARAMETER AccountName + The name of the service account. For gMSA, do not include trailing $. For regular accounts, use the username. + +.PARAMETER AccountType + Type of account: 'gMSA' or 'ServiceAccount'. Default: 'gMSA' + +.PARAMETER Password + Password for regular service accounts. Not used for gMSA accounts. Can be SecureString or plain text. + +.PARAMETER SaJsonPath + Full local path to the sa.json file that RubrikSecurityCloud module will use. + +.PARAMETER OutputXmlPath + Full local path to the output xml service account file (sa-rbksql.xml). + +.PARAMETER TaskName + (Optional) Scheduled task name. Default: CreateRubrikSAFile- + +.PARAMETER KeepArtifacts + If $true, keep the temporary script and task after completion. Default $false = cleanup. + +.EXAMPLE + # Using gMSA + .\createSAcreds.ps1 -Domain AD -AccountName rubrikgmsa -AccountType gMSA -SaJsonPath C:\temp\sa.json -OutputXmlPath C:\temp\sa-rbksql.xml + +.EXAMPLE + # Using regular service account with password prompt + .\createSAcreds.ps1 -Domain AD -AccountName rbksql -AccountType ServiceAccount -Password (Read-Host -AsSecureString -Prompt "Enter SA password") -SaJsonPath C:\Rubrik\scripts\sa.json -OutputXmlPath C:\Rubrik\scripts\sa-real.xml + +.EXAMPLE + # Using regular service account with plain text password + .\createSAcreds.ps1 -Domain AD -AccountName rbksql -AccountType ServiceAccount -Password "MyPassword123" -SaJsonPath C:\Rubrik\scripts\sa.json -OutputXmlPath C:\Rubrik\scripts\sa-real.xml +#> + +param( + [string]$Domain, + [Parameter(Mandatory=$true)][string]$AccountName, + [ValidateSet('gMSA', 'ServiceAccount')][string]$AccountType = 'gMSA', + [object]$Password, + [Parameter(Mandatory=$true)][string]$SaJsonPath, + [Parameter(Mandatory=$true)][string]$OutputXmlPath, + [string]$TaskName = "CreateRubrikSAFile-$((Get-Date).ToString('yyyyMMdd-HHmmss'))", + [switch]$KeepArtifacts +) + +try { + # ---- Parameter validation ---- + if ($AccountType -eq 'ServiceAccount' -and -not $Password) { + # Prompt for password if not provided for service accounts + Write-Host "Password required for service account '$AccountName'" + $Password = Read-Host -AsSecureString -Prompt "Enter password for $AccountName" + } + + if ($AccountType -eq 'gMSA' -and $Password) { + Write-Warning "Password parameter ignored for gMSA accounts" + } + + # ---- Basic validation ---- + if (-not (Test-Path -Path $SaJsonPath)) { throw "SA JSON not found at: $SaJsonPath" } + $tempDir = Join-Path -Path $env:TEMP -ChildPath "CreateRubrikSAFile_$([guid]::NewGuid().ToString().Substring(0,8))" + New-Item -Path $tempDir -ItemType Directory -Force | Out-Null + + $oneShotScript = Join-Path $tempDir "Create-SA-File.ps1" + $logFile = Join-Path $tempDir "Create-SA-File.log" + + # ---- Create the one-shot script that will run under the service account ---- + $oneShotContent = @" +# One-shot script created by create-and-run-one-shot-via-gMSA.ps1 +# Runs RubrikSecurityCloud command to create service-account file + +# Start transcript for detailed logging +Start-Transcript -Path `"$logFile`" -Append + +Write-Output "Script started at: `$(Get-Date)" +Write-Output "Running as user: `$([System.Security.Principal.WindowsIdentity]::GetCurrent().Name)" +Write-Output "PowerShell version: `$(`$PSVersionTable.PSVersion)" + +Try { + Write-Output "Attempting to import RubrikSecurityCloud module..." + Import-Module RubrikSecurityCloud -ErrorAction Stop + Write-Output "Successfully imported RubrikSecurityCloud module" +} Catch { + Write-Error "Failed to import RubrikSecurityCloud module: `$(`$_.Exception.Message)" + Write-Error "Full exception: `$(`$_.Exception | Format-List * | Out-String)" + Stop-Transcript + Exit 2 +} + +Try { + Write-Output "Checking input file: $SaJsonPath" + # Ensure the input file exists + if (-not (Test-Path -Path `"$SaJsonPath`")) { + Write-Error "Input SA JSON not found: $SaJsonPath" + Stop-Transcript + Exit 3 + } + Write-Output "Input file found, size: `$((Get-Item `"$SaJsonPath`").Length) bytes" + + Write-Output "Calling Set-RscServiceAccountFile..." + Write-Output " Input: $SaJsonPath" + Write-Output " Output: $OutputXmlPath" + + Set-RscServiceAccountFile `"$SaJsonPath`" -OutputFilePath `"$OutputXmlPath`" -Verbose + + Write-Output "Set-RscServiceAccountFile completed" + + if (Test-Path -Path `"$OutputXmlPath`") { + Write-Output "Service account XML created successfully: $OutputXmlPath" + Write-Output "Output file size: `$((Get-Item `"$OutputXmlPath`").Length) bytes" + Stop-Transcript + Exit 0 + } else { + Write-Error "Set-RscServiceAccountFile completed but output file not found: $OutputXmlPath" + Write-Error "Checking parent directory: `$(Split-Path `"$OutputXmlPath`")" + if (Test-Path (Split-Path `"$OutputXmlPath`")) { + Write-Output "Parent directory exists, listing contents:" + Get-ChildItem (Split-Path `"$OutputXmlPath`") | ForEach-Object { Write-Output " `$(`$_.Name)" } + } else { + Write-Error "Parent directory does not exist: `$(Split-Path `"$OutputXmlPath`")" + } + Stop-Transcript + Exit 4 + } +} Catch { + Write-Error "Error creating RBK service-account file: `$(`$_.Exception.Message)" + Write-Error "Full exception: `$(`$_.Exception | Format-List * | Out-String)" + Write-Error "Stack trace: `$(`$_.ScriptStackTrace)" + Stop-Transcript + Exit 5 +} +"@ + + # 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 + + # Make sure executable by scheduled task + icacls $oneShotScript /grant "BUILTIN\Administrators:(R,W)" | Out-Null + + # ---- Build Scheduled Task objects ---- + # Construct the UserId based on account type + if ($AccountType -eq 'gMSA') { + $userId = if ([string]::IsNullOrWhiteSpace($Domain)) { "$AccountName`$" } else { "$Domain\$AccountName`$" } + $logonType = 'Password' # For gMSA, use Password logon type + } else { + $userId = if ([string]::IsNullOrWhiteSpace($Domain)) { $AccountName } else { "$Domain\$AccountName" } + $logonType = 'Password' # For regular service accounts, use Password logon type + } + + # 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" + $action = New-ScheduledTaskAction -Execute (Join-Path $env:WINDIR 'System32\WindowsPowerShell\v1.0\powershell.exe') -Argument $psArgs + + # Trigger: once, a short time in the future (1 minute from now) + $startTime = (Get-Date).AddMinutes(1) + $trigger = New-ScheduledTaskTrigger -Once -At $startTime + + # Principal: service account or gMSA + $principal = New-ScheduledTaskPrincipal -UserId $userId -LogonType $logonType -RunLevel Highest + + # Settings: one-shot, don't persist run as logged on user UI + $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -ExecutionTimeLimit (New-TimeSpan -Hours 1) + + $task = New-ScheduledTask -Action $action -Principal $principal -Trigger $trigger -Settings $settings + + # ---- Register the scheduled task ---- + if ($AccountType -eq 'gMSA') { + # For gMSA, register without password (AD will handle authentication) + Register-ScheduledTask -TaskName $TaskName -InputObject $task -Force + Write-Host "Registered scheduled task '$TaskName' to run as gMSA $userId at $startTime." + } else { + # For regular service accounts, register with password + $securePassword = if ($Password -is [SecureString]) { $Password } else { ConvertTo-SecureString $Password -AsPlainText -Force } + Register-ScheduledTask -TaskName $TaskName -InputObject $task -User $userId -Password ([Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePassword))) -Force + Write-Host "Registered scheduled task '$TaskName' to run as service account $userId at $startTime." + } + + # Optionally start immediately (Start-ScheduledTask will ignore trigger time and try to run it) + Start-ScheduledTask -TaskName $TaskName + Write-Host "Started task. Waiting for completion..." + + # ---- Wait for completion and inspect result ---- + $maxWaitSeconds = 600 + $pollInterval = 3 + $elapsed = 0 + $lastResult = $null + $taskCompleted = $false + + while ($true) { + Start-Sleep -Seconds $pollInterval + $elapsed += $pollInterval + + $info = Get-ScheduledTaskInfo -TaskName $TaskName -ErrorAction SilentlyContinue + if ($null -eq $info) { + Write-Warning "Could not query task info yet." + } else { + # LastTaskResult returns Win32 error code; 0 = success + $lastResult = $info.LastTaskResult + $state = $info.State + Write-Host "Task state: '$state'; LastResult: $lastResult" + + # Task is complete if: + # 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 + if (($state -eq 'Ready' -or $state -eq 'Disabled' -or $state -eq 'Unknown' -or [string]::IsNullOrEmpty($state)) -and + ($lastResult -ne $null -and $lastResult -ne 267009)) { + $taskCompleted = $true + break + } + + if ($state -eq 'Running') { + Write-Host "Task still running..." + } + } + + if ($elapsed -ge $maxWaitSeconds) { + throw "Timed out waiting for scheduled task to finish (waited $maxWaitSeconds seconds)." + } + } + + Write-Host "Task completed after $elapsed seconds." + + # ---- Check exit status and output ---- + if ($lastResult -eq 0) { + Write-Host "Task finished successfully (LastTaskResult=0)." + if (Test-Path -Path $OutputXmlPath) { + Write-Host "Found output XML: $OutputXmlPath" + } else { + Write-Warning "Task indicated success but output file not found at $OutputXmlPath" + } + } else { + Write-Host "Scheduled task finished with non-zero LastTaskResult: $lastResult" + + # Display log file contents for troubleshooting + if (Test-Path -Path $logFile) { + Write-Host "`n--- Log file contents ($logFile) ---" + Get-Content -Path $logFile | ForEach-Object { Write-Host $_ } + Write-Host "--- End of log file ---`n" + } else { + Write-Warning "Log file not found at: $logFile" + } + + throw "Scheduled task finished with non-zero LastTaskResult: $lastResult. Check Event Viewer > Applications and Services Logs > Microsoft > Windows > TaskScheduler for details, or review the log output above." + } + + # ---- Cleanup ---- + if (-not $KeepArtifacts) { + Write-Host "Cleaning up task and temporary files..." + try { Unregister-ScheduledTask -TaskName $TaskName -Confirm:$false -ErrorAction SilentlyContinue } catch {} + try { Remove-Item -Path $tempDir -Recurse -Force -ErrorAction SilentlyContinue } catch {} + Write-Host "Cleanup complete." + } else { + Write-Host "Kept task '$TaskName' and temporary script at: $oneShotScript" + } + +} catch { + Write-Error "ERROR: $($_.Exception.Message)" + throw +} diff --git a/start.cmd b/start.cmd index cf8241f..ee7e0ce 100644 --- a/start.cmd +++ b/start.cmd @@ -1,3 +1,3 @@ :: filepath: c:\Rubrik\Scripts\start.cmd @echo off -powershell -ExecutionPolicy Bypass -File "%~dp0backup.ps1" \ No newline at end of file +powershell -ExecutionPolicy Bypass -File "%~dp0backup.ps1" %* \ No newline at end of file