Providers/AD/Private/Get-ADTestDnsZoneObject.ps1
|
function Get-ADTestDnsZoneObject { <# .SYNOPSIS Finds the directory object behind a DNS zone, wherever the partition it lives in .DESCRIPTION An Active Directory-integrated zone is a directory object, which is what lets this provider stamp it with the seed tag and prove later that it created it. Finding that object is not as simple as asking for it, because it does not live in the domain naming context: a zone replicated to the domain sits under `DC=DomainDnsZones,<domain>`, one replicated to the forest under `DC=ForestDnsZones,<forest>`, and a legacy one under `CN=MicrosoftDNS,CN=System`. A `Get-ADObject` with no search base only looks in the domain naming context, so it finds none of the first two and silently returns nothing. That is not a loud failure: the zone gets created, the tag never gets written, and teardown then refuses to remove a zone this module made because it cannot prove it. That happened, which is why the search base is explicit here and why all three partitions are tried. .PARAMETER ZoneName The DNS zone name, as the DNS server reports it. .PARAMETER DomainDN The domain's distinguished name. Defaults to the connected domain's. .PARAMETER ForestDN The forest root's distinguished name, which is where DC=ForestDnsZones hangs. Defaults to the connected domain's forest, and to DomainDN when that is unknown, which is exact only in a single-domain forest: a child domain that searched ForestDnsZones under its own DN would miss every forest-replicated zone. .OUTPUTS The directory object, or nothing when the zone is not directory-integrated. .EXAMPLE PS> Get-ADTestDnsZoneObject -ZoneName 'zz-test-lab.ad.contoso.com' DESCRIPTION: Finds the object so it can be tagged or checked OUTPUT: An ADObject with its adminDescription USE CASE: Called when the zone is created, and again at teardown to prove ownership .NOTES Author: Jeffrey Stuhr Blog: https://www.techbyjeff.net LinkedIn: https://www.linkedin.com/in/jeffrey-stuhr-034214aa/ #> [CmdletBinding()] [OutputType([object])] param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ZoneName, [Parameter()] [string]$DomainDN, [Parameter()] [string]$ForestDN ) if (-not $DomainDN -or -not $ForestDN) { $domain = Get-ADTestDomain if (-not $DomainDN) { $DomainDN = $domain.DomainDN } if (-not $ForestDN) { $ForestDN = $domain.ForestDN } } if (-not $ForestDN) { $ForestDN = $DomainDN } # The forest partition hangs off the forest root, not off the connected domain. The two # are the same DN in a single-domain forest, which is why searching under the domain # looked right for as long as it was only tried there. $searchBases = @( "DC=DomainDnsZones,$DomainDN" "DC=ForestDnsZones,$ForestDN" "CN=MicrosoftDNS,CN=System,$DomainDN" ) foreach ($searchBase in $searchBases) { try { $found = Get-ADObject -LDAPFilter "(&(objectClass=dnsZone)(name=$ZoneName))" ` -SearchBase $searchBase -Properties adminDescription -ErrorAction Stop | Select-Object -First 1 if ($found) { Write-Verbose "Zone '$ZoneName' found under $searchBase" return $found } } catch { Write-Verbose "Partition $searchBase did not answer: $($_.Exception.Message)" } } Write-Verbose "No directory object found for zone '$ZoneName'" return $null } |