Check/Fix DNS Permissions

Check the permissions of the records that match the computer name specified and optionally change them so that the computer account for the current AD object for that computer has rights to update the record.
Run on a computer that has the ActiveDirectory PowerShell mdoule installed like a domain controller. Permissions for Domain controller DNS records will not be changed,
Use when machines have been rebuilt and they cannot update their own DNS record because it is owned by the account for the previous build which no longer exists in AD so the current computer does not have permissions when it wants to register the IP address against its own DNS record
NOTE: It is recommended you make a backup of your DNS with the Backup DNS Zone script before running this script with Fix set to Yes.
Version 1.1.7
Created on 2019-10-03
Modified on 2022-10-06
Created by Guy Leech
Downloads: 30

The Script Copy Script Copied to clipboard
<#
    Find Active Directory DNS records with unresolved SIDs and replace with the machine account

    @guyrleech 2019

    Based on code from https://www.heelpbook.net/2018/powershell-find-and-add-dns-record-permissions/
#>

[CmdletBinding()]

Param
(
    [Parameter(Mandatory=$true,HelpMessage='Enter computer name or pattern to check DNS record(s) for')]
    [string]$computername ,    
    [Parameter(Mandatory=$false,HelpMessage='Whether to fix issues found (True or False)')]
    [ValidateSet('Yes','No')]
    [string]$fixParameter = 'No'
)

$ErrorActionPreference = 'Stop'
$VerbosePreference = 'SilentlyContinue'
$DebugPreference = 'SilentlyContinue'

[bool]$fix = $( if( $fixParameter -eq 'Yes' ) { $true } else { $false } )

Import-Module -Name ActiveDirectory -ErrorAction Stop -Verbose:$false

[string[]]$reservedNames = @( 'DomainDnsZones' , 'ForestDnsZones' , '@' )
[int]$fixedCount = 0
$thisDomain = Get-ADDomain
[string]$DomainName = $thisDomain.DNSroot
[string]$AdIntegrationType = 'Domain'
[string]$DomainDn = $thisDomain.DistinguishedName

## get domain controllers so we can avoid lest we break anything big style
[hashtable]$domainControllers = @{}
Get-ADGroupMember -Identity 'Domain Controllers' | ForEach-Object `
{
     $dc = $_
     $domainControllers.Add( $dc.DistinguishedName , $dc.SID )
}

[string]$adpath = "AD:\DC=$DomainName,CN=MicrosoftDNS,DC=$AdIntegrationType`DnsZones,$DomainDn"

Write-Verbose "AD path is `"$adpath`""

## exclude _ldap _kerberos _dc etc
[array]$results = @( Get-ChildItem $adpath | Where-Object {  $_.Name -like $computername -and $_.Name[0] -ne '_' -and $_.Name.IndexOf('.') -lt 0 -and $reservedNames -notcontains $_.Name } | ForEach-Object `
{
    $record = $_
    Write-Verbose -Message "Checking $($record.Name)"
    try
    {
        $computer = Get-ADComputer -Identity $record.Name -ErrorAction SilentlyContinue -ErrorVariable ComputerError
    }
    catch
    {
        $computer = $null ## may be a manual entry so don't report as missing
    }
    if( $computer )
    {
        if( $domainControllers[ $computer.DistinguishedName ] )
        {
            Write-Warning "$($computer.Name) is a domain controller so skipping due to risk"
        }
        else
        {
            [string]$ADPath = "ActiveDirectory:://RootDSE/$($record.DistinguishedName)"
            $ACL = Get-Acl -Path $ADPath -ErrorAction SilentlyContinue
            if( $ACL )
            {
                $result = New-Object -TypeName PSObject
                Add-Member -InputObject $result -MemberType NoteProperty -Name 'Computer' -Value $computer.Name
                [string]$machineAccount = $((($computer.DNSHostName -split '\.')[1..0]) -join '\') + '$'
                [int]$foundSelf = 0
                [bool]$aclChanged = $false
                [int]$unresolvedSIDs = 0

                if( $ACL.Owner -ne $machineAccount )
                {
                    Write-Verbose -Message "Found incorrect owner $($ACL.Owner) on $($computer.Name)"
                    Add-Member -InputObject $result -MemberType NoteProperty -Name 'Incorrect Owner' -Value $ACL.Owner
                    if( $fix )
                    {
                        $ACL.SetOwner( [System.Security.Principal.NTAccount]$machineAccount )
                        $aclChanged = $true
                    }
                }
                ForEach( $ACE in $ACL.Access )
                {
                    if( $ACE.IdentityReference -eq $machineAccount )
                    {
                        $foundSelf++
                    }
                    elseif( $ACE.IdentityReference -match '^S\-1\-5\-21\-\d{10}-\d{10}-\d{10}-\d+$' )
                    {
                        Write-Verbose -Message "Found unresolved SID $($ACE.IdentityReference) in ACL for $($computer.Name)"
                        $unresolvedSIDs++
                        if( $fix )
                        {
                            $removal = $ACL.RemoveAccessRule( $ACE )
                            if( $removal )
                            {
                                $aclChanged = $true
                            }
                            else
                            {
                                Write-Warning "Failed to remove $($ACE.IdentityReference) from ACE on $($computer.Name)"
                            }
                        }
                    }
                }
                if( $unresolvedSIDs )
                {
                    Add-Member -InputObject $result -MemberType NoteProperty -Name 'Unresolved SIDs' -Value $unresolvedSIDs
                }
                if( ! $foundSelf )
                {
                    Write-Verbose -Message "Failed to find $machineAccount in ACL for $($computer.Name)"
                    Add-Member -InputObject $result -MemberType NoteProperty -Name 'Missing Machine Account' -Value 'Yes'
                    if( $fix )
                    {
                        $Acl.AddAccessRule( ( New-Object System.DirectoryServices.ActiveDirectoryAccessRule( $Computer.Sid , 'GenericAll', 'Allow' ) ) )
                        $aclChanged = $true
                    }
                }
                if( ( $result.PSObject.Properties.GetEnumerator()|Measure-Object|Select-Object -ExpandProperty Count ) -gt 1 )
                {
                    $result
                }
                if( $aclChanged -and $fix )
                {
                    $newACL = Set-Acl -Path $ADPath -AclObject $Acl -Passthru
                    if( $newACL )
                    {
                        $fixedCount++
                    }
                }
            }
            else
            {
                Write-Warning "Failed to get ACL for $($record.distinguishedName)"
            }
        }
    }
})

if( $results -and $results.Count )
{
    [string]$status = "Found $($results.Count)"
    if( $fixedCount )
    {
        $status += " and fixed $fixedCount"
    }
    Write-Output -InputObject "$status DNS record permission issues:"
    $results | Format-Table -AutoSize
}
else
{
    Write-Output -InputObject 'Found no DNS record permission issues'
}