<# .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 }