Created
March 21, 2025 19:08
-
-
Save davidlu1001/ac538bfb307f7c1c79e4738d3fe89a8d to your computer and use it in GitHub Desktop.
Test-gMSAConfiguration.ps1
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Save this script as Test-gMSAConfiguration.ps1 | |
param( | |
[Parameter(Mandatory = $true)] | |
[string]$gMSAName, | |
[Parameter(Mandatory = $false)] | |
[string]$ServiceName = "", | |
[Parameter(Mandatory = $false)] | |
[switch]$FixIssues | |
) | |
$ErrorActionPreference = 'Continue' | |
$results = @() | |
Write-Host "Starting gMSA Configuration Test Tool..." -ForegroundColor Cyan | |
Write-Host "Testing gMSA account: $gMSAName" -ForegroundColor Cyan | |
if ($ServiceName) { | |
Write-Host "Target service: $ServiceName" -ForegroundColor Cyan | |
} | |
Write-Host "------------------------------------------------" -ForegroundColor Cyan | |
# Test function | |
function Test-Item { | |
param($Name, $Test) | |
Write-Host "Testing: $Name..." -NoNewline | |
try { | |
$result = & $Test | |
if ($result -eq $true) { | |
Write-Host "Passed" -ForegroundColor Green | |
return @{Name = $Name; Result = "Passed"; Details = ""; FixAction = $null } | |
} | |
else { | |
Write-Host "Failed" -ForegroundColor Red | |
return @{Name = $Name; Result = "Failed"; Details = $result; FixAction = $FixAction } | |
} | |
} | |
catch { | |
Write-Host "Error" -ForegroundColor Red | |
return @{Name = $Name; Result = "Error"; Details = $_.Exception.Message; FixAction = $null } | |
} | |
} | |
# Function to sanitize gMSA name (handle various input formats) | |
function Get-SanitizedAccountName { | |
param ( | |
[string]$AccountName | |
) | |
# Remove realm suffix if any | |
$accountName = $AccountName -replace '\..*$', '' | |
# If account name ends with $, keep it, otherwise add it | |
if (-not $accountName.EndsWith('$')) { | |
$accountName = "$accountName`$" | |
} | |
return $accountName | |
} | |
# 1. Check if PowerShell is running as Administrator | |
$results += Test-Item -Name "Administrator Privileges Check" -Test { | |
$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent() | |
$principal = New-Object System.Security.Principal.WindowsPrincipal($identity) | |
$adminRole = [System.Security.Principal.WindowsBuiltInRole]::Administrator | |
if ($principal.IsInRole($adminRole)) { | |
$true | |
} | |
else { | |
"Script is not running with administrator privileges. Please restart as administrator." | |
} | |
} | |
# 2. Check if Active Directory module is available | |
$results += Test-Item -Name "AD Module Check" -Test { | |
if (Get-Module -ListAvailable -Name ActiveDirectory) { | |
Import-Module ActiveDirectory -ErrorAction SilentlyContinue | |
$true | |
} | |
else { | |
"Active Directory PowerShell module is not installed. Install RSAT-AD-PowerShell feature." | |
} | |
} | |
# 3. Check domain connectivity | |
$results += Test-Item -Name "Domain Connectivity Check" -Test { | |
try { | |
$domainController = (Get-ADDomainController -Discover -ErrorAction Stop).HostName | |
if ($domainController) { | |
$true | |
} | |
else { | |
"Unable to connect to any domain controller" | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 4. Check KDS Root Key | |
$results += Test-Item -Name "KDS Root Key Check" -Test { | |
try { | |
$kdsRootKeys = Get-KdsRootKey -ErrorAction Stop | |
if ($kdsRootKeys) { | |
$true | |
} | |
else { | |
"No KDS Root Key found. Need to create one: Add-KdsRootKey -EffectiveImmediately" | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 5. Check if gMSA account exists | |
$results += Test-Item -Name "gMSA Account Existence" -Test { | |
try { | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
$account = Get-ADServiceAccount -Identity $accountName -ErrorAction Stop | |
if ($account) { | |
$true | |
} | |
else { | |
"gMSA account '$accountName' does not exist" | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 6. Check if current computer has permission to use the gMSA | |
$results += Test-Item -Name "Computer Permission Check" -Test { | |
try { | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
$computerName = $env:COMPUTERNAME | |
$testResult = Test-ADServiceAccount -Identity $accountName -ErrorAction Stop | |
if ($testResult) { | |
$true | |
} | |
else { | |
"Current computer '$computerName' does not have permission to use gMSA '$accountName'. Check PrincipalsAllowedToRetrieveManagedPassword property." | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 7. Check if gMSA is installed on this computer | |
$results += Test-Item -Name "gMSA Installation Check" -Test { | |
try { | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
# Use Test-ADServiceAccount to check if gMSA is installed | |
$isInstalled = Test-ADServiceAccount -Identity $accountName -ErrorAction Stop | |
if ($isInstalled) { | |
$true | |
} | |
else { | |
"gMSA '$accountName' is not installed on this computer. Run Install-ADServiceAccount -Identity $accountName" | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 8. Check SPN configuration | |
$results += Test-Item -Name "SPN Configuration Check" -Test { | |
try { | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
$account = Get-ADServiceAccount -Identity $accountName -Properties ServicePrincipalNames -ErrorAction Stop | |
if ($account.ServicePrincipalNames -and $account.ServicePrincipalNames.Count -gt 0) { | |
$true | |
} | |
else { | |
"gMSA account has no SPNs configured. This may not be an issue unless you need Kerberos authentication." | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 9. Test service account password provider | |
$results += Test-Item -Name "Password Provider Check" -Test { | |
try { | |
$keyIsoService = Get-Service -Name 'KeyIso' -ErrorAction Stop | |
if ($keyIsoService.Status -eq 'Running') { | |
$true | |
} | |
else { | |
"Key Distribution Service (KeyIso) is not running. Current status: $($keyIsoService.Status)" | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 10. Check NetLogon service | |
$results += Test-Item -Name "NetLogon Service Check" -Test { | |
try { | |
$netlogonService = Get-Service -Name 'Netlogon' -ErrorAction Stop | |
if ($netlogonService.Status -eq 'Running') { | |
$true | |
} | |
else { | |
"NetLogon service is not running. Current status: $($netlogonService.Status)" | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 11. Check gMSA password age | |
$results += Test-Item -Name "gMSA Password Age Check" -Test { | |
try { | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
$account = Get-ADServiceAccount -Identity $accountName -Properties PasswordLastSet -ErrorAction Stop | |
$passwordAge = (Get-Date) - $account.PasswordLastSet | |
if ($passwordAge.TotalDays -lt 30) { | |
$true | |
} | |
else { | |
"gMSA password is $($passwordAge.TotalDays.ToString('0.0')) days old. This is not necessarily an issue, but be aware that gMSA passwords typically rotate every 30 days." | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 12. If a service name is provided, check service configuration | |
if ($ServiceName) { | |
$results += Test-Item -Name "Service Configuration Check" -Test { | |
try { | |
$service = Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'" -ErrorAction Stop | |
if ($null -eq $service) { | |
return "Service '$ServiceName' does not exist" | |
} | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
$serviceAccount = $service.StartName | |
# Check if service is using the specified gMSA | |
if ($serviceAccount -like "*$accountName*" -or $serviceAccount -like "*$($accountName.TrimEnd('$'))*") { | |
$true | |
} | |
else { | |
"Service '$ServiceName' is not using the specified gMSA account. Current logon account: $serviceAccount" | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 13. Check service startup dependencies | |
$results += Test-Item -Name "Service Dependencies Check" -Test { | |
try { | |
$serviceDependencies = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Name "DependOnService" -ErrorAction SilentlyContinue).DependOnService | |
if ($null -eq $serviceDependencies) { | |
$serviceDependencies = @() | |
} | |
$requiredDependencies = @("KeyIso", "Netlogon") | |
$missingDependencies = $requiredDependencies | Where-Object { $serviceDependencies -notcontains $_ } | |
if ($missingDependencies.Count -eq 0) { | |
$true | |
} | |
else { | |
"Service is missing recommended dependencies: $($missingDependencies -join ', '). Adding these dependencies can improve startup reliability." | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 14. Check service start mode | |
$results += Test-Item -Name "Service Start Mode Check" -Test { | |
try { | |
$service = Get-WmiObject -Class Win32_Service -Filter "Name='$ServiceName'" -ErrorAction Stop | |
if ($service.StartMode -eq "Auto") { | |
# Check if it's delayed start | |
$startType = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -ErrorAction SilentlyContinue).Start | |
$delayedAutostart = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -ErrorAction SilentlyContinue).DelayedAutostart | |
if ($startType -eq 2 -and $delayedAutostart -eq 1) { | |
$true | |
} | |
else { | |
"Service is set to Automatic Start but not Delayed Start. Using Delayed Start can improve reliability for gMSA services." | |
} | |
} | |
else { | |
"Service start mode is $($service.StartMode), not Automatic." | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
} | |
# 15. Check computer password age | |
$results += Test-Item -Name "Computer Password Age Check" -Test { | |
try { | |
$computerAccount = Get-ADComputer -Identity $env:COMPUTERNAME -Properties PasswordLastSet -ErrorAction Stop | |
$passwordAge = (Get-Date) - $computerAccount.PasswordLastSet | |
if ($passwordAge.TotalDays -lt 30) { | |
$true | |
} | |
else { | |
"Computer account password is $($passwordAge.TotalDays.ToString('0.0')) days old. If this is approaching your domain's maximum password age, consider resetting it." | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 16. Verify DNS resolution | |
$results += Test-Item -Name "DNS Resolution Check" -Test { | |
try { | |
$domainName = (Get-ADDomain -ErrorAction Stop).DNSRoot | |
$dnsResult = Resolve-DnsName -Name $domainName -Type A -ErrorAction Stop | |
if ($dnsResult) { | |
$true | |
} | |
else { | |
"Unable to resolve domain DNS name" | |
} | |
} | |
catch { | |
$_.Exception.Message | |
} | |
} | |
# 17. Check for Windows updates that affect gMSA functionality | |
$results += Test-Item -Name "Windows Updates Check" -Test { | |
try { | |
$osVersion = (Get-WmiObject -Class Win32_OperatingSystem).Version | |
$criticalUpdatesFound = $false | |
# Known critical updates for gMSA on Windows Server 2012 R2 | |
if ($osVersion -like "6.3*") { | |
# Windows Server 2012 R2 | |
$criticalUpdates = @( | |
"KB3156418", # May 2016 Security update | |
"KB3149090" # Known gMSA fix | |
) | |
$installedUpdates = Get-HotFix | Select-Object -ExpandProperty HotFixID | |
$missingUpdates = $criticalUpdates | Where-Object { $installedUpdates -notcontains $_ } | |
if ($missingUpdates.Count -gt 0) { | |
$criticalUpdatesFound = $true | |
"Missing critical updates for gMSA functionality: $($missingUpdates -join ', ')" | |
} | |
} | |
if (-not $criticalUpdatesFound) { | |
$true | |
} | |
} | |
catch { | |
"Unable to check for Windows updates: $_" | |
} | |
} | |
# Output summary | |
Write-Host "`n------------------------------------------------" -ForegroundColor Cyan | |
Write-Host "Test Summary:" -ForegroundColor Cyan | |
Write-Host "------------------------------------------------" -ForegroundColor Cyan | |
$passedTests = ($results | Where-Object { $_.Result -eq "Passed" }).Count | |
$totalTests = $results.Count | |
Write-Host "Passed $passedTests of $totalTests tests" -ForegroundColor ([System.ConsoleColor]$(if ($passedTests -eq $totalTests) { "Green" } else { "Yellow" })) | |
$failedTests = $results | Where-Object { $_.Result -ne "Passed" } | |
if ($failedTests) { | |
Write-Host "`nThe following tests failed:" -ForegroundColor Red | |
foreach ($test in $failedTests) { | |
Write-Host "- $($test.Name): $($test.Details)" -ForegroundColor Red | |
} | |
# Suggested remediation steps | |
Write-Host "`nRemediation steps:" -ForegroundColor Yellow | |
# Handle common failures with specific remediation advice | |
if (($failedTests | Where-Object { $_.Name -eq "Computer Permission Check" }).Count -gt 0) { | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
Write-Host "1. Ensure this computer is added to the security group allowed to use the gMSA:" -ForegroundColor Yellow | |
Write-Host " Get-ADServiceAccount -Identity $accountName -Properties PrincipalsAllowedToRetrieveManagedPassword | Select-Object -ExpandProperty PrincipalsAllowedToRetrieveManagedPassword" -ForegroundColor Yellow | |
Write-Host " # Then add this computer to that group, or run:" -ForegroundColor Yellow | |
Write-Host " Set-ADServiceAccount -Identity $accountName -PrincipalsAllowedToRetrieveManagedPassword 'Domain\Group'" -ForegroundColor Yellow | |
} | |
if (($failedTests | Where-Object { $_.Name -eq "gMSA Installation Check" }).Count -gt 0) { | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
Write-Host "2. Install the gMSA on this computer:" -ForegroundColor Yellow | |
Write-Host " Install-ADServiceAccount -Identity $accountName" -ForegroundColor Yellow | |
} | |
if (($failedTests | Where-Object { $_.Name -eq "KDS Root Key Check" }).Count -gt 0) { | |
Write-Host "3. Create a KDS Root Key (run on domain controller):" -ForegroundColor Yellow | |
Write-Host " Add-KdsRootKey -EffectiveImmediately" -ForegroundColor Yellow | |
Write-Host " # For immediate use (in lab environments only):" -ForegroundColor Yellow | |
Write-Host " Add-KdsRootKey -EffectiveTime ((Get-Date).AddHours(-10))" -ForegroundColor Yellow | |
} | |
if (($failedTests | Where-Object { $_.Name -eq "AD Module Check" }).Count -gt 0) { | |
Write-Host "4. Install the Active Directory PowerShell module:" -ForegroundColor Yellow | |
Write-Host " Install-WindowsFeature RSAT-AD-PowerShell" -ForegroundColor Yellow | |
} | |
if (($failedTests | Where-Object { $_.Name -eq "Password Provider Check" }).Count -gt 0) { | |
Write-Host "5. Start and configure the Key Distribution Service:" -ForegroundColor Yellow | |
Write-Host " Start-Service KeyIso" -ForegroundColor Yellow | |
Write-Host " Set-Service KeyIso -StartupType Automatic" -ForegroundColor Yellow | |
} | |
if (($failedTests | Where-Object { $_.Name -eq "Service Dependencies Check" }).Count -gt 0 -and $ServiceName) { | |
Write-Host "6. Add recommended service dependencies:" -ForegroundColor Yellow | |
Write-Host " `$currentDependencies = (Get-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName' -Name 'DependOnService' -ErrorAction SilentlyContinue).DependOnService" -ForegroundColor Yellow | |
Write-Host " `$newDependencies = `$currentDependencies + @('KeyIso', 'Netlogon')" -ForegroundColor Yellow | |
Write-Host " Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName' -Name 'DependOnService' -Value `$newDependencies" -ForegroundColor Yellow | |
} | |
if (($failedTests | Where-Object { $_.Name -eq "Service Start Mode Check" }).Count -gt 0 -and $ServiceName) { | |
Write-Host "7. Set service to Delayed Start for improved reliability:" -ForegroundColor Yellow | |
Write-Host " Set-ItemProperty -Path 'HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName' -Name 'DelayedAutostart' -Value 1 -Type DWORD" -ForegroundColor Yellow | |
} | |
# If FixIssues flag is specified, attempt to fix common issues automatically | |
if ($FixIssues) { | |
Write-Host "`nAttempting to fix issues automatically..." -ForegroundColor Cyan | |
# Fix: Install gMSA if needed | |
if (($failedTests | Where-Object { $_.Name -eq "gMSA Installation Check" }).Count -gt 0) { | |
$accountName = Get-SanitizedAccountName -AccountName $gMSAName | |
Write-Host "Installing gMSA $accountName..." -NoNewline | |
try { | |
Install-ADServiceAccount -Identity $accountName -ErrorAction Stop | |
Write-Host "Success" -ForegroundColor Green | |
} | |
catch { | |
Write-Host "Failed" -ForegroundColor Red | |
Write-Host " Error: $_" -ForegroundColor Red | |
} | |
} | |
# Fix: Start KeyIso service if not running | |
if (($failedTests | Where-Object { $_.Name -eq "Password Provider Check" }).Count -gt 0) { | |
Write-Host "Starting KeyIso service..." -NoNewline | |
try { | |
Start-Service KeyIso -ErrorAction Stop | |
Set-Service KeyIso -StartupType Automatic -ErrorAction Stop | |
Write-Host "Success" -ForegroundColor Green | |
} | |
catch { | |
Write-Host "Failed" -ForegroundColor Red | |
Write-Host " Error: $_" -ForegroundColor Red | |
} | |
} | |
# Fix: Set service dependencies if service name is provided | |
if (($failedTests | Where-Object { $_.Name -eq "Service Dependencies Check" }).Count -gt 0 -and $ServiceName) { | |
Write-Host "Setting service dependencies..." -NoNewline | |
try { | |
$currentDependencies = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Name "DependOnService" -ErrorAction SilentlyContinue).DependOnService | |
if ($null -eq $currentDependencies) { | |
$currentDependencies = @() | |
} | |
$newDependencies = $currentDependencies | |
if ($newDependencies -notcontains "KeyIso") { $newDependencies += "KeyIso" } | |
if ($newDependencies -notcontains "Netlogon") { $newDependencies += "Netlogon" } | |
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Name "DependOnService" -Value $newDependencies -ErrorAction Stop | |
Write-Host "Success" -ForegroundColor Green | |
} | |
catch { | |
Write-Host "Failed" -ForegroundColor Red | |
Write-Host " Error: $_" -ForegroundColor Red | |
} | |
} | |
# Fix: Set service to delayed start if needed | |
if (($failedTests | Where-Object { $_.Name -eq "Service Start Mode Check" }).Count -gt 0 -and $ServiceName) { | |
Write-Host "Setting service to delayed start..." -NoNewline | |
try { | |
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\$ServiceName" -Name "DelayedAutostart" -Value 1 -Type DWORD -ErrorAction Stop | |
Write-Host "Success" -ForegroundColor Green | |
} | |
catch { | |
Write-Host "Failed" -ForegroundColor Red | |
Write-Host " Error: $_" -ForegroundColor Red | |
} | |
} | |
} | |
} | |
else { | |
Write-Host "All tests passed!" -ForegroundColor Green | |
Write-Host "gMSA configuration looks good. If services still fail to start, try using the Fix-gMSAService.ps1 script." -ForegroundColor Green | |
} | |
# Create service logon validation script | |
Write-Host "`n------------------------------------------------" -ForegroundColor Cyan | |
Write-Host "Manual Service Reset Commands" -ForegroundColor Cyan | |
Write-Host "------------------------------------------------" -ForegroundColor Cyan | |
Write-Host "You can use the following commands to manually reset a service's gMSA credentials:" -ForegroundColor Yellow | |
if ($ServiceName) { | |
Write-Host "# Reset service '$ServiceName' credentials:" | |
Write-Host "`$service = Get-WmiObject -Class Win32_Service -Filter `"Name='$ServiceName'`"" | |
Write-Host "`$accountName = `$service.StartName" | |
Write-Host "`$result = `$service.Change(`$null, `$null, `$null, `$null, `$null, `$null, `$accountName, `$null, `$null, `$null, `$null)" | |
Write-Host "if (`$result.ReturnValue -eq 0) { Write-Host 'Success' } else { Write-Host 'Failed' }" | |
Write-Host "Start-Service -Name '$ServiceName'" | |
} | |
else { | |
Write-Host "# Reset service credentials:" | |
Write-Host "`$serviceName = 'YourServiceName'" | |
Write-Host "`$service = Get-WmiObject -Class Win32_Service -Filter `"Name='`$serviceName'`"" | |
Write-Host "`$accountName = `$service.StartName" | |
Write-Host "`$result = `$service.Change(`$null, `$null, `$null, `$null, `$null, `$null, `$accountName, `$null, `$null, `$null, `$null)" | |
Write-Host "if (`$result.ReturnValue -eq 0) { Write-Host 'Success' } else { Write-Host 'Failed' }" | |
Write-Host "Start-Service -Name `$serviceName" | |
} | |
Write-Host "`nThese commands refresh the service's gMSA credentials without changing the account." -ForegroundColor Cyan | |
# If we have a service, provide restart command | |
if ($ServiceName) { | |
Write-Host "`n# Quick restart command for '$ServiceName':" -ForegroundColor Yellow | |
Write-Host "Restart-Service -Name '$ServiceName' -Force" | |
} | |
# Output environment info | |
Write-Host "`n------------------------------------------------" -ForegroundColor Cyan | |
Write-Host "Environment Information" -ForegroundColor Cyan | |
Write-Host "------------------------------------------------" -ForegroundColor Cyan | |
Write-Host "Computer Name: $env:COMPUTERNAME" | |
Write-Host "Windows Version: $((Get-WmiObject -Class Win32_OperatingSystem).Caption) ($((Get-WmiObject -Class Win32_OperatingSystem).Version))" | |
try { | |
$domainInfo = Get-ADDomain | |
Write-Host "Domain: $($domainInfo.DNSRoot)" | |
Write-Host "Domain Controller: $((Get-ADDomainController -Discover).HostName)" | |
Write-Host "Domain Functional Level: $($domainInfo.DomainMode)" | |
} | |
catch { | |
Write-Host "Unable to retrieve domain information: $_" -ForegroundColor Red | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment