If you have ever worked with SPN attributes, you will know that by default standard accounts do not have access to update the SPN automatically by themselves, this also follows on from the article here
Me any software or application that tries to register on SPN will fail, as the relevant permission is not granted to normal accounts, this means either a member of account operators or domain administrators needs to complete this action manually.
However, that is a lot of administration overhead, and it can sometimes easily be forgotten because the majority of the time depending on what you’re doing missing an SPN will not be detrimental to the servers performance, until April 2025 when invalid SPN will be blocked.
If you wish to grant permission to service accounts or other accounts, this you need this command:
dsacls "<Service_Account_DN_Path>" /G SELF:RPWP;"servicePrincipalName"
This is absolutely fine for the old couple of service accounts, but what if you need to do this on bulk?
Luckily, for this particular example, I have created a script, obviously, please ensure you check the code before you just download a script from the Internet and read it on your production systems 🤣
The operation script is very simple, it is also menu driven which means you have the option to do the following:
- Check accounts from a certain organization unit that do not have this permission set
- Check accounts from a certain organizational unit and then sent those permissions where not currently set (this is the option that writes data)
- Retrieve a list of all the valid SPN’s for accounts in the specified Organizational unit
# Import required modules
Import-Module ActiveDirectory
# Function to check SPN permissions
function Check-SPNPermissions {
param (
[string]$OUPath
)
try {
$serviceAccounts = Get-ADUser -Filter * -SearchBase $OUPath -Properties nTSecurityDescriptor
$results = @()
foreach ($account in $serviceAccounts) {
$acl = $account.nTSecurityDescriptor.Access
$hasSPNPermission = $acl | Where-Object {
$_.IdentityReference -eq "SELF" -and
$_.ObjectType -eq "servicePrincipalName" -and
$_.ActiveDirectoryRights -match "ReadProperty, WriteProperty"
}
if (-not $hasSPNPermission) {
$results += [PSCustomObject]@{
AccountName = $account.Name
DistinguishedName = $account.DistinguishedName
}
}
}
if ($results.Count -gt 0) {
$outputPath = Join-Path $PSScriptRoot "MissingSPNPermissions.txt"
$results | Format-Table -AutoSize | Out-File $outputPath
Write-Host "Found $($results.Count) accounts missing SPN permissions. Results saved to: $outputPath"
} else {
Write-Host "All service accounts have proper SPN permissions."
}
return $results
}
catch {
Write-Error "Error checking permissions: $_"
return $null
}
}
# Function to set SPN permissions
function Set-SPNPermissions {
param (
[array]$Accounts
)
foreach ($account in $Accounts) {
try {
$command = "dsacls `"$($account.DistinguishedName)`" /G SELF:RPWP;`"servicePrincipalName`""
Invoke-Expression $command
Write-Host "Successfully set SPN permissions for: $($account.AccountName)"
}
catch {
Write-Error "Failed to set permissions for $($account.AccountName): $_"
}
}
}
# Function to get configured SPNs
function Get-ConfiguredSPNs {
param (
[string]$OUPath
)
try {
$serviceAccounts = Get-ADUser -Filter * -SearchBase $OUPath -Properties ServicePrincipalName
$results = @()
foreach ($account in $serviceAccounts) {
$results += [PSCustomObject]@{
AccountName = $account.Name
DistinguishedName = $account.DistinguishedName
SPNs = $account.ServicePrincipalName -join '; '
HasSPNs = if ($account.ServicePrincipalName) { $true } else { $false }
}
}
$outputPath = Join-Path $PSScriptRoot "ConfiguredSPNs.txt"
$results | Format-Table -AutoSize | Out-File $outputPath
Write-Host "SPN configuration details saved to: $outputPath"
return $results
}
catch {
Write-Error "Error retrieving SPNs: $_"
return $null
}
}
# Main menu
function Show-Menu {
Clear-Host
Write-Host "=== SPN Permission Management ==="
Write-Host "1. Check SPN Permissions"
Write-Host "2. Check and Set Missing SPN Permissions"
Write-Host "3. View Configured SPNs"
Write-Host "4. Exit"
}
# Main script
do {
Show-Menu
$choice = Read-Host "Enter your choice (1-4)"
switch ($choice) {
"1" {
$ouPath = Read-Host "Enter the OU path (e.g., 'OU=Service Accounts,DC=domain,DC=com')"
Check-SPNPermissions -OUPath $ouPath
pause
}
"2" {
$ouPath = Read-Host "Enter the OU path (e.g., 'OU=Service Accounts,DC=domain,DC=com')"
$missingPermissions = Check-SPNPermissions -OUPath $ouPath
if ($missingPermissions) {
$confirm = Read-Host "Do you want to set permissions for these accounts? (Y/N)"
if ($confirm -eq 'Y') {
Set-SPNPermissions -Accounts $missingPermissions
}
}
pause
}
"3" {
$ouPath = Read-Host "Enter the OU path (e.g., 'OU=Service Accounts,DC=domain,DC=com')"
Get-ConfiguredSPNs -OUPath $ouPath
pause
}
"4" {
Write-Host "Exiting..."
break
}
default {
Write-Host "Invalid choice. Please try again."
pause
}
}
} while ($choice -ne "4")