Public/New-NSPADServiceAccount.ps1
|
function New-NSPADServiceAccount { <# .SYNOPSIS Creates a gMSA that the given computers may use, unless it already exists. Idempotent; dry-run aware (Invoke-NSPStep). .DESCRIPTION Checks first, over LDAP: an existing gMSA of that name is returned unchanged (with a warning if the allowed computers differ); the forest needs a usable KDS root key (one is created with Add-KdsRootKey on a DC, and gMSAs can use it about 10 hours later). A KDS key that can't be seen may mean this account can't read it - -SkipKdsCheck goes ahead anyway. The account is created with New-ADServiceAccount (AD module) against the DC that Get-NSPADDomainInfo found: AES Kerberos only, enabled, the allowed computers as PrincipalsAllowedToRetrieveManagedPassword. Then install it on each of those computers (Install-NSPADServiceAccount) - a fresh gMSA can take a few minutes to replicate. .EXAMPLE Set-NSPDryRun -Enabled $false New-NSPADServiceAccount -Name 'svc_transfer' -AllowedComputer 'APP01' -Description 'Runs the nightly file transfer' #> [CmdletBinding(SupportsShouldProcess)] [OutputType('NSP.ActiveDirectory.ServiceAccount')] param( [Parameter(Mandatory)][ValidateLength(1, 15)][ValidatePattern('^[A-Za-z0-9_-]+\$?$')][string]$Name, [Parameter(Mandatory)][string[]]$AllowedComputer, [string]$Description, [string]$DNSHostName, [ValidateRange(1, 3650)][int]$PasswordIntervalDays = 30, [switch]$SkipKdsCheck, [PSTypeName('NSP.ActiveDirectory.DomainInfo')]$DomainInfo ) $bare = $Name.TrimEnd('$') if (-not $DomainInfo) { $DomainInfo = Get-NSPADDomainInfo } $computers = foreach ($c in $AllowedComputer) { $found = Get-NSPADComputer -Name $c -DomainInfo $DomainInfo if (-not $found) { throw "No computer account '$c' in $($DomainInfo.DNSRoot)." } $found } $existing = Get-NSPADServiceAccount -Name $bare -DomainInfo $DomainInfo if ($existing) { $missing = @($computers | Where-Object { @($existing.AllowedPrincipals.Sid) -notcontains $_.SID } | ForEach-Object Name) if ($missing.Count) { Write-Warning "$($existing.Logon) already exists and does not list $($missing -join ', ') directly as allowed to retrieve its password (a group may still grant it - see Test-NSPADServiceAccount)." } Write-Host " gMSA '$bare' already exists ($($existing.DistinguishedName)) - skipping create." -ForegroundColor Gray return $existing } if (-not $SkipKdsCheck) { $kds = Get-NSPADKdsRootKeyStatus -DomainInfo $DomainInfo if (-not $kds.Usable) { $why = if ($kds.Visible) { 'the KDS root key is not usable yet (gMSAs can use it about 10 hours after Add-KdsRootKey)' } else { 'no KDS root key is visible - create one on a DC (Add-KdsRootKey -EffectiveImmediately, then wait about 10 hours), or pass -SkipKdsCheck if this account just cannot read it' } throw "Can't create gMSA '$bare': $why." } } $hostName = if ($DNSHostName) { $DNSHostName } else { "$bare.$($DomainInfo.DNSRoot)" } if (-not $PSCmdlet.ShouldProcess("$($DomainInfo.NetBIOSName)\$bare`$", "Create gMSA usable by $(@($computers.Name) -join ', ')")) { return } Use-NSPADDependency Assert-NSPADCommand New-ADServiceAccount $principalDns = @($computers.DistinguishedName) $result = Invoke-NSPStep -Description "Create gMSA '$bare' (usable by $(@($computers.Name) -join ', ')) on $($DomainInfo.Server)" ` -Commands @("New-ADServiceAccount -Name '$bare' -DNSHostName '$hostName' -PrincipalsAllowedToRetrieveManagedPassword <$($computers.Count) computer(s)> -ManagedPasswordIntervalInDays $PasswordIntervalDays -KerberosEncryptionType AES128,AES256 -Server '$($DomainInfo.Server)'") ` -Action { $params = @{ Name = $bare; DNSHostName = $hostName; Enabled = $true PrincipalsAllowedToRetrieveManagedPassword = $principalDns ManagedPasswordIntervalInDays = $PasswordIntervalDays KerberosEncryptionType = 'AES128', 'AES256' Server = $DomainInfo.Server } if ($Description) { $params.Description = $Description } New-ADServiceAccount @params } if ($result.Ran) { return (Get-NSPADServiceAccount -Name $bare -DomainInfo $DomainInfo) } } |