Skip to content

Instantly share code, notes, and snippets.

@davidlu1001
Created March 21, 2025 19:08
Show Gist options
  • Save davidlu1001/ac538bfb307f7c1c79e4738d3fe89a8d to your computer and use it in GitHub Desktop.
Save davidlu1001/ac538bfb307f7c1c79e4738d3fe89a8d to your computer and use it in GitHub Desktop.
Test-gMSAConfiguration.ps1
# 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