Skip to content

Instantly share code, notes, and snippets.

@davidlu1001
Created March 21, 2025 19:08
Show Gist options
  • Save davidlu1001/5029b933e3ebe624350efd06e3bdbc09 to your computer and use it in GitHub Desktop.
Save davidlu1001/5029b933e3ebe624350efd06e3bdbc09 to your computer and use it in GitHub Desktop.
Fix-gMSAService.ps1
# gMSA Service AutoFix Solution
# This script creates a reliable startup fix for services using gMSA accounts
# Save as Fix-gMSAService.ps1
param(
[Parameter(Mandatory = $true)]
[string]$ServiceName,
[Parameter(Mandatory = $false)]
[int]$StartupDelaySeconds = 120,
[Parameter(Mandatory = $false)]
[switch]$InstallOnly = $false,
[Parameter(Mandatory = $false)]
[switch]$Verbose = $false
)
# Function to write detailed logs
function Write-VerboseLog {
param([string]$Message)
if ($Verbose) {
Write-Host $Message -ForegroundColor Cyan
}
}
function Install-ServiceFix {
param(
[string]$ServiceName,
[int]$StartupDelaySeconds
)
Write-Host "Installing gMSA service fix for '$ServiceName'..." -ForegroundColor Yellow
# ----- Validation -----
# Check if the service exists
$service = Get-Service -Name $ServiceName -ErrorAction SilentlyContinue
if ($null -eq $service) {
Write-Error "Service '$ServiceName' does not exist. Please verify the service name."
return $false
}
# Check if service is using a gMSA account
$serviceWmi = Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'"
$accountName = $serviceWmi.StartName
Write-Host "Service logon account: $accountName" -ForegroundColor Cyan
if (-not ($accountName -like "*$")) {
Write-Warning "The service does not appear to be using a gMSA account (account name doesn't end with $)."
$continue = Read-Host "Continue anyway? (Y/N)"
if ($continue -ne "Y") {
return $false
}
}
# ----- Create Fix Script -----
Write-VerboseLog "Creating service fix script..."
# Uniquely identify this service fix
$serviceId = $ServiceName -replace '[^a-zA-Z0-9]', ''
$scriptName = "gMSAFix_$serviceId.ps1"
$scriptPath = "$env:ProgramData\$scriptName"
# Create the fix script content
$scriptContent = @"
# gMSA Service Fix Script for $ServiceName
# Generated $(Get-Date)
`$ErrorActionPreference = 'Stop'
`$logFile = "`$env:TEMP\${serviceId}_gMSA_fix.log"
# Create log function
function Write-Log {
param(`$message)
`$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
"`$timestamp - `$message" | Out-File -FilePath `$logFile -Append -Force
# Also write to event log for tracking
try {
if (-not [System.Diagnostics.EventLog]::SourceExists("gMSAServiceFix")) {
New-EventLog -LogName Application -Source "gMSAServiceFix"
}
Write-EventLog -LogName Application -Source "gMSAServiceFix" -EntryType Information -EventId 1000 -Message `$message
} catch {
# Continue even if event log writing fails
}
}
# Clear previous log if it exists
if (Test-Path `$logFile) {
Remove-Item `$logFile -Force
}
Write-Log "Starting gMSA service fix for '$ServiceName'"
try {
# First, ensure Active Directory services are available
Write-Log "Ensuring AD services are available..."
# Test domain connectivity with timeout
`$domainReady = `$false
`$attempt = 1
`$maxAttempts = 10
while (-not `$domainReady -and `$attempt -le `$maxAttempts) {
try {
Write-Log "AD connectivity attempt `$attempt of `$maxAttempts..."
# Try to get domain controller
`$dc = [System.DirectoryServices.ActiveDirectory.Domain]::GetComputerDomain().DomainControllers[0].Name
Write-Log "Successfully contacted domain controller: `$dc"
`$domainReady = `$true
}
catch {
Write-Log "Domain not yet available. Waiting 5 seconds... (Error: `$_)"
Start-Sleep -Seconds 5
`$attempt++
}
}
if (-not `$domainReady) {
Write-Log "Failed to connect to domain after `$maxAttempts attempts. Will try to reset service anyway."
}
# Check if service exists
Write-Log "Checking if service exists..."
`$service = Get-Service -Name "$ServiceName" -ErrorAction SilentlyContinue
if (`$null -eq `$service) {
Write-Log "ERROR: Service '$ServiceName' not found!"
exit 1
}
# Get current service status
Write-Log "Current service status: `$(`$service.Status)"
# If service is running, we don't need to fix it
if (`$service.Status -eq "Running") {
Write-Log "Service is already running. No fix needed."
exit 0
}
# Get service WMI object
Write-Log "Getting service WMI object..."
`$serviceWmi = Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'"
`$accountName = `$serviceWmi.StartName
Write-Log "Service logon account: `$accountName"
# Reset the service credentials (this is the key fix)
Write-Log "Resetting service credentials..."
`$result = `$serviceWmi.Change(
`$null, # DisplayName
`$null, # PathName
`$null, # ServiceType
`$null, # ErrorControl
`$null, # StartMode
`$null, # DesktopInteract
`$accountName, # StartName (reuse current account name)
`$null, # StartPassword (null forces Windows to retrieve gMSA password)
`$null, # LoadOrderGroup
`$null, # LoadOrderGroupDependencies
`$null # ServiceDependencies
)
if (`$result.ReturnValue -eq 0) {
Write-Log "Successfully reset service credentials"
# Try to start the service
Write-Log "Attempting to start service..."
try {
Start-Service -Name "$ServiceName"
Start-Sleep -Seconds 3
# Check service status again
`$service = Get-Service -Name "$ServiceName"
Write-Log "Service status after start attempt: `$(`$service.Status)"
if (`$service.Status -eq "Running") {
Write-Log "SUCCESS: Service started successfully!"
exit 0
} else {
Write-Log "WARNING: Service not in Running state. Current state: `$(`$service.Status)"
# One more attempt with a delayed start, in case we need to wait for dependencies
Write-Log "Attempting one more delayed start..."
Start-Sleep -Seconds 10
Start-Service -Name "$ServiceName" -ErrorAction SilentlyContinue
Start-Sleep -Seconds 5
# Final check
`$service = Get-Service -Name "$ServiceName"
if (`$service.Status -eq "Running") {
Write-Log "SUCCESS: Service started successfully on second attempt!"
exit 0
} else {
Write-Log "ERROR: Service failed to start, final status: `$(`$service.Status)"
exit 1
}
}
} catch {
Write-Log "ERROR starting service: `$_"
exit 1
}
} else {
Write-Log "ERROR: Failed to reset service credentials. Return value: `$(`$result.ReturnValue)"
exit 1
}
} catch {
Write-Log "CRITICAL ERROR: `$_"
exit 1
}
"@
# Save the script
$scriptContent | Out-File -FilePath $scriptPath -Encoding UTF8 -Force
Write-Host "Created service fix script at: $scriptPath" -ForegroundColor Green
# ----- Create Scheduled Task -----
Write-VerboseLog "Creating scheduled task..."
$taskName = "gMSAFix_$serviceId"
# Check if task already exists and remove it
$existingTask = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue
if ($existingTask) {
Write-VerboseLog "Removing existing task..."
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false
}
# Create a startup trigger with specified delay
$trigger = New-ScheduledTaskTrigger -AtStartup
$trigger.Delay = "PT${StartupDelaySeconds}S" # Format: PT<seconds>S
# Create the action to run the PowerShell script
$action = New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File `"$scriptPath`""
# Run with highest privileges as SYSTEM
$principal = New-ScheduledTaskPrincipal -UserId "SYSTEM" -LogonType ServiceAccount -RunLevel Highest
# Create task settings
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -RunOnlyIfNetworkAvailable
# Register the task
Register-ScheduledTask -TaskName $taskName -Action $action -Trigger $trigger -Principal $principal -Settings $settings -Force | Out-Null
Write-Host "Created scheduled task '$taskName' to run at system startup with a ${StartupDelaySeconds}-second delay" -ForegroundColor Green
# ----- Test Key Services -----
Write-VerboseLog "Testing key services..."
$keyIsoService = Get-Service -Name "KeyIso" -ErrorAction SilentlyContinue
if ($keyIsoService.Status -ne "Running") {
Write-Warning "The Key Isolation service (KeyIso) is not running. This service is critical for gMSA functionality."
Write-Host "Attempting to start KeyIso service..." -ForegroundColor Yellow
Start-Service -Name "KeyIso" -ErrorAction SilentlyContinue
}
# ----- Add Service Dependency -----
# This ensures our service starts after the Key Distribution Service
Write-VerboseLog "Setting service dependencies..."
try {
$currentDependencies = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Name "DependOnService" -ErrorAction SilentlyContinue).DependOnService
if ($null -eq $currentDependencies) {
$currentDependencies = @()
}
# Add KeyIso dependency if it doesn't exist
if (-not $currentDependencies.Contains("KeyIso")) {
$newDependencies = $currentDependencies + @("KeyIso")
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Name "DependOnService" -Value $newDependencies
Write-Host "Added KeyIso service dependency to ensure proper startup order" -ForegroundColor Green
}
}
catch {
Write-Warning "Could not modify service dependencies: $_"
}
return $true
}
function Test-ServiceNow {
param([string]$ServiceName)
Write-Host "Testing service fix immediately..." -ForegroundColor Yellow
# Get the script path
$serviceId = $ServiceName -replace '[^a-zA-Z0-9]', ''
$scriptPath = "$env:ProgramData\gMSAFix_$serviceId.ps1"
if (-not (Test-Path $scriptPath)) {
Write-Error "Fix script not found at: $scriptPath"
return $false
}
# Execute the script
try {
Write-Host "Executing fix script..." -ForegroundColor Cyan
& PowerShell.exe -NoProfile -ExecutionPolicy Bypass -File "$scriptPath"
# Check result
$service = Get-Service -Name $ServiceName
if ($service.Status -eq "Running") {
Write-Host "SUCCESS: Service is now running!" -ForegroundColor Green
return $true
}
else {
Write-Host "WARNING: Service is not running. Current status: $($service.Status)" -ForegroundColor Yellow
return $false
}
}
catch {
Write-Error "Error executing fix script: $_"
return $false
}
}
# Main execution
if (Install-ServiceFix -ServiceName $ServiceName -StartupDelaySeconds $StartupDelaySeconds) {
Write-Host "`ngMSA service fix for '$ServiceName' has been successfully installed." -ForegroundColor Green
Write-Host "The fix will run automatically after every system reboot with a ${StartupDelaySeconds}-second delay." -ForegroundColor Cyan
# Test immediately unless InstallOnly is specified
if (-not $InstallOnly) {
Write-Host "`n=================================================" -ForegroundColor Yellow
if (Test-ServiceNow -ServiceName $ServiceName) {
Write-Host "`nImmediate fix test passed. The service is now running." -ForegroundColor Green
Write-Host "The fix will also run automatically after every reboot." -ForegroundColor Green
}
else {
Write-Host "`nImmediate fix test did not start the service." -ForegroundColor Yellow
Write-Host "Please check the service configuration and try again." -ForegroundColor Yellow
Write-Host "Run this command for detailed manual testing:" -ForegroundColor Yellow
Write-Host "PowerShell.exe -NoProfile -ExecutionPolicy Bypass -File `"$env:ProgramData\gMSAFix_$($ServiceName -replace '[^a-zA-Z0-9]', '').ps1`"" -ForegroundColor Cyan
}
}
# Display usage instructions
Write-Host "`n=================================================" -ForegroundColor Yellow
Write-Host "USAGE INSTRUCTIONS:" -ForegroundColor Yellow
Write-Host "1. To fix the service manually anytime:"
Write-Host " PowerShell.exe -NoProfile -ExecutionPolicy Bypass -File `"$env:ProgramData\gMSAFix_$($ServiceName -replace '[^a-zA-Z0-9]', '').ps1`""
Write-Host "2. To uninstall the fix:"
Write-Host " Unregister-ScheduledTask -TaskName `"gMSAFix_$($ServiceName -replace '[^a-zA-Z0-9]', '')`" -Confirm:`$false"
Write-Host "3. To view fix logs:"
Write-Host " Get-Content -Path `"`$env:TEMP\$($ServiceName -replace '[^a-zA-Z0-9]', '')_gMSA_fix.log`""
Write-Host "=================================================" -ForegroundColor Yellow
}
else {
Write-Host "Failed to install gMSA service fix." -ForegroundColor Red
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment