Tidy up and new file to create SA creds

This commit is contained in:
2025-10-07 17:59:04 +01:00
parent 4182f24f3d
commit 1ac04e0b79
3 changed files with 278 additions and 3 deletions

274
createSAcreds.ps1 Normal file
View File

@@ -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-<timestamp>
.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
}