Powershell : Managing SPN Write Permissions

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:

  1. Check accounts from a certain organization unit that do not have this permission set
  2. Check accounts from a certain organizational unit and then sent those permissions where not currently set (this is the option that writes data)
  3. Retrieve a list of all the valid SPN’s for accounts in the specified Organizational unit
This is the script in action, here we are choosing option 1:


Script : SPNAnalyer.ps1

# 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")

Previous Post Next Post

نموذج الاتصال