Commands/Get-GMSACredential.ps1
Function Get-GMSACredential{ <# .SYNOPSIS Returns the GMSA PSCredential based on the identity name of the account .DESCRIPTION Retrieves the password of the GMSA from AD and uses that to create a PSCredential .PARAMETER Identity Identity of the GMSA account .PARAMETER Domain Domain logon name of the account .EXAMPLE $Path = 'OU=SERVICE ACCOUNTS,OU=CORP,DC=corp,DC=contoso,DC=com' $adGroup = New-ADGroup -Name GMSAAdmin -GroupCategory Security -Path $Path -GroupScope Global -PassThru Add-ADGroupMember -Identity GMSAAdmin -Members $env:USERNAME klist.exe purge # may refresh ad groups. If it doesnt logout and relogin. Try{ $MyGMSA = New-ADServiceAccount -Name MyGMSA -Path $path -PrincipalsAllowedToRetrieveManagedPassword $adGroup -DNSHostName MyGMSA -Passthru }Catch{ Add-KdsRootKey -EffectiveTime ((get-date).addhours(-10)) $MyGMSA = New-ADServiceAccount -Name MyGMSA -Path $path -PrincipalsAllowedToRetrieveManagedPassword $adGroup -DNSHostName MyGMSA -Passthru } $G = Get-GMSACredential -Identity MyGMSA Test-Credential $g - .NOTES .Author: Ryan Ephgrave and Jeff Scripter #> Param( [Parameter(Mandatory=$true)] [string]$Identity, [string]$Domain ) if ([String]::IsNullOrWhiteSpace($domain)){ $Domain = (Get-CimInstance WIN32_ComputerSystem).Domain } $DirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry $searcher = New-Object System.DirectoryServices.DirectorySearcher -ArgumentList $DirectoryEntry $searcher.Filter = "(&(name=$($Identity))(ObjectCategory=msDS-GroupManagedServiceAccount))" $searcher.SearchRoot.AuthenticationType = 'Sealing' $Null = $searcher.PropertiesToLoad.Add('Name') $Null = $searcher.PropertiesToLoad.Add('msDS-ManagedPassword') $Null = $searcher.PropertiesToLoad.Add('msDS-GroupMSAMembership') $Accounts = $searcher.FindAll() foreach($a in $accounts){ #Assuming only one Group with Manage Password Permissions $GroupPermissions = $a.Properties.'msds-groupmsamembership' [Byte[]]$Groupblob = $GroupPermissions.Foreach({$PSItem}) $GroupSid = [Security.Principal.SecurityIdentifier]::new($Groupblob,$Groupblob.Length - 28) $WindowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent() # Check if user has AD Group to manage password if ( -not $WindowsIdentity.Groups.Contains($GroupSid)) { $searcher.Filter = "(objectSid=$($GroupSid.value))" $Null = $searcher.PropertiesToLoad.Add('Name') $Null = $searcher.PropertiesToLoad.Add('member') $searcher.SearchRoot.AuthenticationType = 'Sealing' $Group = $searcher.FindAll() $SysInfo = New-Object -ComObject ADSystemInfo $UserPath = $SysInfo.GetType().InvokeMember("UserName", "GetProperty", $Null, $SysInfo, $Null) if ($Group.properties.member -contains $UserPath){ Write-Warning -Message "$($WindowsIdentity.name) does not Locally contain Group $($Group.path). Try Killing kerberos tickets." }else{ Write-Warning -Message "$($WindowsIdentity.name) is not in $($Group.path)." } } #Retrieve Password if($a.Properties.'msds-managedpassword'){ $pw = $a.Properties.'msds-managedpassword' [Byte[]]$byteBlob = $pw.Foreach({$PSItem}) $MemoryStream = New-Object System.IO.MemoryStream -ArgumentList (,$byteBlob) $Reader = New-Object System.IO.BinaryReader -ArgumentList $MemoryStream # have to move the reader to the pw offset $Version = $Reader.ReadInt16() $Reserved = $Reader.ReadInt16() $Length = $Reader.ReadInt32() $CurrentPWOffset = $Reader.ReadInt16() $PreviousPWOffset = $Reader.ReadInt16() $QueryPWInterval = $Reader.ReadInt16() $UnChangedPWInterval = $Reader.ReadInt16() $Length = $byteBlob.Length - $CurrentPWOffset $stringBuilder = New-Object System.Text.StringBuilder -ArgumentList $Length for($i = $CurrentPWOffset ; $i -le $Length; $i += [System.Text.UnicodeEncoding]::CharSize){ $currentChar = [System.BitConverter]::ToChar($byteBlob, $i) if($currentChar -eq [char]::MinValue) { break; } [void]$stringBuilder.Append($currentChar) #Write-Verbose -Message "$I - $($byteBlob[$i]) - $currentChar" } $QueryInterval = [TimeSpan]::FromTicks([System.BitConverter]::ToUInt64($byteBlob,$UnChangedPWInterval)) Write-Verbose -Message "Password valie for $($QueryInterval.TotalDays)" $TimeSpan = [TimeSpan]::FromTicks([System.BitConverter]::ToUInt64($byteBlob,$UnChangedPWInterval)) Write-Verbose -Message "Password Changes in $($QueryInterval.TotalDays)" $Credential = ( New-Object PSCredential -ArgumentList @( "$Domain\$identity$", (ConvertTo-SecureString $stringBuilder.ToString() -AsPlainText -Force) )) return $Credential } } } |