DnsStig.psm1

Function Set-DnsServerStig {
    <#
        .SYNOPSIS
            Implements all STIGs contained in this module within the specified forest.
 
        .DESCRIPTION
            The Set-DnsServerStig cmdlet implements all of the STIGs in this module.
 
        .PARAMETER Forest
            The DNS Forest to configure.
 
        .PARAMETER RemoveProhibitedRecords
            Option to remove found prohibited records of type HINFO, RP, TXT, and LOC.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerStig
 
            Implements all STIGs contained in this module within the specified forest, but does not removed prohibited record types.
 
        .EXAMPLE
            PS C:\>Set-DnsServerStig -RemoveProhibitedRecords
 
            Implements all STIG settings contained in this module and removes prohibited record types.
     
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
    #>

    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [switch]$RemoveProhibitedRecords = $false,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty  
    )

    Begin {
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Starting DNS STIG"

        if ($RemoveProhibitedRecords)
        {
            Remove-DnsServerProhibitedRecords -Forest $Forest -Credential $Credential
        }
        else
        {
            Get-DnsServerProhibitedRecords -Forest $Forest -Credential $Credential | ForEach-Object {
                Write-Warning ($_.Record | Out-String)
            }
        }

        $IPv6Servers = Get-DnsServersRunningIPv6 -Forest $Forest -Credential $Credential 
        Write-Warning -Message "These servers are running IPv6 ensure this is required. $($IPv6Servers | FL | Out-String)"
        

        Get-InactiveDnsServers -Forest $Forest | ForEach-Object {
            Write-Warning "Inactive server: $_"
        }

        Set-DnsServerZoneSecureUpdates -Forest $Forest -Credential $Credential
        Set-DnsServerDisableRootHints -Forest $Forest -Credential $Credential
        Set-DnsServerRecursionAndForwarders -Forest $Forest -Credential $Credential
        Set-DnsServerCryptoFolderPermissions -Forest $Forest -Credential $Credential
        Set-DnsServerZoneTransfers -Forest $Forest -Credential $Credential
        Set-DnsServerLogPermissions -Forest $Forest -Credential $Credential
        Set-DnsServerLogging -Forest $Forest -Credential $Credential
        Set-DnsServerVersionQuery -Forest $Forest -Credential $Credential

        Remove-DnsServerZoneIPv6LinkLocalAddresses -Forest $Forest -Credential $Credential
    }

    End {}
}

Function Remove-DnsServerProhibitedRecords {
    <#
        .SYNOPSIS
            The HINFO, RP, TXT and LOC RR types must not be used in the zone SOA.
 
        .DESCRIPTION
            The Remove-DnsServerProhibitedRecords cmdlet gets and then removes all prohibited records from each zone in the forest.
 
        .PARAMETER Forest
            The DNS Forest to configure.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Remove-DnsServerProhibitedRecords
 
            Removes all of the prohibited records in the current user forest.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-SI-000004
            Rule ID
                SV-73169r1
            Vuln ID
                V-58739
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter(Position=1)]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {    
    }

    Process
    {
        
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Removing DNS Server Prohibited Entries."

        Get-DnsServerProhibitedRecords -Forest $Forest -Credential $Credential | ForEach-Object {
            $Record = ($_.Record | Out-String)
            Write-Warning "Removing record $Record" 

            try
            {
                $Server = Resolve-DnsName -Name $_.ZoneName -ErrorAction Stop | Select-Object -ExpandProperty PrimaryServer -ErrorAction SilentlyContinue
                
                if ([System.String]::IsNullOrEmpty($Server)) {
                    Write-Verbose -Message "PrimaryServer attribute was null, using the zone name for the server."
                    $Server = $_.ZoneName
                }

                Remove-DnsServerResourceRecord -ZoneName $_.ZoneName -ComputerName $Server -InputObject $_.Record -Force -Confirm:$false
            }
            catch [Exception]
            {
                Write-Warning "Error removing record $Record`: $($_.ToString())"
            }
        }

        Write-Host "Removing prohibited entries complete."
    }

    End {        
    }
}

Function Get-DnsServerProhibitedRecords {
    <#
        .SYNOPSIS
            The HINFO, RP, TXT and LOC RR types must not be used in the zone SOA.
 
        .DESCRIPTION
            The Get-DnsServerProhibitedRecords cmdlet gets all prohibited records from each zone in the forest.
 
        .PARAMETER Forest
            The DNS Forest to search.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Get-DnsServerProhibitedRecords
 
            Gets all of the prohibited records in the current user forest.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            PSObject[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-SI-000004
            Rule ID
                SV-73169r1
            Vuln ID
                V-58739
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0, ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {        
    }

    Process
    {
        Write-Host "Getting DNS Server prohibited entries."

        $BadRecords = @()

        Get-ForestDnsZones -ZoneType All -LookupType Forward -Forest $Forest -Credential $Credential | ForEach-Object {
            
            try
            {
                Write-Host "Getting entries for zone $($_.ZoneName)"
                $Zone = $_.ZoneName
                $Server = Resolve-DnsName -Name $_.ZoneName -ErrorAction Stop | Select-Object -ExpandProperty PrimaryServer -ErrorAction SilentlyContinue
                
                if ([System.String]::IsNullOrEmpty($Server)) {
                    Write-Verbose -Message "PrimaryServer attribute was null, using the zone name for the server."
                    $Server = $_.ZoneName
                }

                $Records = @(Get-DnsServerResourceRecord -ComputerName $Server -ZoneName $_.ZoneName | Where-Object {$_.RecordType -in @("Hinfo","RP","TXT","LOC")})

                if ($Records.Count -gt 0)
                {
                    foreach ($Record in $Records)
                    {
                        $BadRecords += @{Record=($Record);ZoneName=$_.ZoneName}
                    }
                }
            }
            catch [Exception]
            {
                Write-Warning "Error getting resource records for $Zone : $($_.ToString())"
            }
        }

        Write-Output -InputObject $BadRecords
    }

    End {
    }
}

Function Get-DnsServersRunningIPv6 {
    <#
        .SYNOPSIS
            When IPv6 protocol is installed, the server must also be configured to answer for IPv6 AAAA records.
 
        .DESCRIPTION
            The Get-DnsServersRunningIPv6 checks each DNS server in the forest to see if it has IPv6 enabled. If it does, but does not host AAAA records, it is returned as part of an array.
 
        .PARAMETER Forest
            The DNS Forest to test.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Get-DnsServersRunningIPv6
 
            Gets all of the DNS servers in the forest running IPv6 and not hosting AAAA records.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            PSObject[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
     
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-CM-000028
            Rule ID
                SV-73057r1
            Vuln ID
                V-58627
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {        
    }

    Process 
    {
        Write-Host "Getting DNS Servers running IPv6 and not hosting AAAA records."

        #0xFFFFFFFF - IPv6 Disabled On All Interfaces
        #0xFFFFFFFE - IPv6 Enabled only on tunnel interfaces
        #0xFFFFFFEF - IPv6 Disabled On Tunnel Interfaces, Enabled On All Others
        #0xFFFFFFEE - IPv6 Disabled On Loopback Interface, Enabled On All Others
        #0xFFFFFFDF - IPv6 Disabled, Prefer IPv6 over IpV4
        #0xFFFFFFDE - IPv6 Enabled Only On Tunnel Interfaces, Prefer IPv6 of IPv4
        #0xFFFFFFCF - IPv6 Enabled On All Non Tunnel Interfaces, Prefer IPv6 over IPv4
        #0xFFFFFFCE - IPv6 Disabled On Loopback Interface, Prefer IPv6 over IPv4

        #0x000000FF - IPv6 Disabled On All Interfaces
        #0x00000020 - IPv6 Prefer IPv4 over IPv6 by changing entries in prefix policy table = 100000
        #0x00000010 - IPv6 Disabled on LAN and PPP interfaces = 010000
        #0x00000008 - Disable Teredo = 001000
        #0x00000004 - Disable ISATAP = 000100
        #0x00000002 - Disable 6to4 = 000010
        #0x00000001 - IPv6 Disabled on Tunnel Interfaces including ISATAP, 6to4 and Teredo = 000001

        $DnsServers = Get-ForestDnsServers -Forest $Forest -Credential $Credential
        $Zones = Get-ForestDnsZones -Forest $Forest -LookupType Forward -Credential $Credential
        $BadZones = @()

        foreach ($Server in $DnsServers) 
        {
            $RegPropertyExists = Invoke-Command -ComputerName $Server -ScriptBlock ${function:Test-RegistryKeyProperty} -ArgumentList "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters","DisabledComponents" -Credential $Credential

            if ($RegPropertyExists)
            {
                $State = Invoke-Command -ComputerName $Server -ScriptBlock {Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\Tcpip6\Parameters" -Name DisabledComponents} -Credential $Credential
                $DisabledValues = @([System.Convert]::ToString(0xFFFFFFFF, 16), [System.Convert]::ToString(0xFFFFFFFE, 16), [System.Convert]::ToString(0x00000011, 16))
            }

            #If IPv6 isn't disabled on the entire system check each individual adapter
            if (-not $RegPropertyExists -or (-not $DisabledValues.Contains([System.Convert]::ToString($State.DisabledComponents, 16))))
            {
                #Will return the number of network adapters with IPv6 Binding enabled
                $Adapters = @(Invoke-Command -ComputerName $Server -ScriptBlock {Get-NetAdapter | Where-Object{ (Get-NetAdapterBinding -InterfaceDESCRIPTION $_.InterfaceDESCRIPTION -ErrorAction SilentlyContinue | Where-Object {$_.ComponentID -eq "ms_tcpip6" -and $_.Enabled -eq $true})}} -Credential $Credential)
                
                if ($Adapters.Count -gt 0)
                {
                    Get-SpecificServerDnsZones -ComputerName $Server -LookupType Forward | ForEach-Object {
                        if (@(Get-DnsServerResourceRecord -ComputerName $Server -RRType AAAA -ZoneName $_.ZoneName).Count -eq 0)
                        {
                            $BadZones += @{ComputerName = $Server;ZoneName=$_.ZoneName;Adapters=$Adapters}
                        }
                    }
                }
            }
        }

        
        if ($BadZones.Count -gt 0)
        {
            Write-Warning -Message "Found servers without AAAA records that have IPv6 enabled."
        }
        
        Write-Output -InputObject $BadZones
    }

    End {
    }
}

Function Set-DnsServerNotifySecondaryServers {
    <#
        .SYNOPSIS
            The Windows 2012 DNS Server must use DNS Notify to prevent denial of service through increase in workload.
 
        .DESCRIPTION
            The Set-DnsServerNotifySecondaryServers cmdlet ensures the Notify secondary servers is enabled for any DNS zone that allows zone transfers.
 
        .PARAMETER Forest
            The DNS Forest to configure.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerNotifySecondaryServers
 
            Ensures Notify is enabled for any DNS servers allowing zone transfers to secondaries.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 12/8/2015
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-SC-000027
            Rule ID
                SV-73129r1
            Vuln ID
                V-58699
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Setting secondary server notification settings."

        $Servers = Get-ForestDnsServers -Forest $Forest -Credential $Credential

        foreach ($Server in $Servers)
        {
            Get-SpecificServerDnsZones -ComputerName $Server -ZoneType Primary -Credential $Credential | ForEach-Object {
                if ($_.SecureSecondaries -ne "NoTransfer")
                {
                    Set-DnsServerPrimaryZone -Name $_.ZoneName -ComputerName $Server -Notify Notify
                }
            }
        }

        Write-Host "Completed setting secondary server notification settings."
    }

    End {    
    }
}

Function Remove-DnsServerWINSForwardLookup {
    <#
        .SYNOPSIS
            WINS lookups must be disabled on the Windows 2012 DNS Server.
 
        .DESCRIPTION
            The Remove-DnsServerWINSForwardLookup disabled WINS forward lookup on each DNS server in the forest.
 
        .PARAMETER Forest
            The DNS Forest to configure.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Remove-DnsServerWINSForwardLookup
 
            Disables WINS lookups on all DNS servers in the forest.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-SC-000006
            Rule ID
                SV-73091r1
            Vuln ID
                V-58661
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {        
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Setting WINS server settings."

        $Servers = Get-ForestDnsServers -Forest $Forest

        foreach ($Server in $Servers)
        {
            Get-SpecificServerDnsZones -ComputerName $Server -ZoneType Primary -LookupType Forward | ForEach-Object {
                #TODO: Set WINS Setting
            }
        }

        Write-Host "Completed WINS server settings."
    }

    End {    
    }
}

Function Set-DnsServerCryptoFolderPermissions {
    <#
        .SYNOPSIS
            The Windows 2012 DNS Server must be configured to enforce authorized access to the corresponding private key.
 
        .DESCRIPTION
            The Set-DnsServerCryptoFolderPermissions sets %ALLUSERSPROFILE%\Microsoft\Crypto folder, subfolders, and files to Full Control for SYSTEM and Administrators and removes all other privileges.
            The owner for the folder, subfolders, and files is also set to BUILTIN\Administrators.
 
        .PARAMETER Forest
            The DNS Forest to configure.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
         
        .EXAMPLE
            PS C:\>Set-DnsServerCryptoFolderPermissions
 
            Sets The permissions and owner for the crypto directory.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-IA-000006
                WDNS-IA-000007
                WDNS-IA-000008
            Rule ID
                SV-73071r1
                SV-73073r1
                SV-73075r1
            Vuln ID
                V-58641
                V-58643
                V-58645
            Severity
                CAT II
#>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )
    
    Begin {
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Setting Dns Server crypto folder owner and permissions."

        $Servers = Get-ForestDnsServers -Forest $Forest -Credential $Credential
        $Path = "$env:ALLUSERSPROFILE\Microsoft\Crypto"
        [System.Security.AccessControl.FileSystemAccessRule[]]$Rules = New-CryptoFolderAccessRuleSet

        foreach ($Server in $Servers)
        {
            Write-Host "Setting file permissions on $Server"

            $ServerPath = "\\$Server\" + $Path.Replace(":\","$\")
            Set-FileSecurity -Path $ServerPath -AccessRules $Rules -ReplaceAll -ForceChildInheritance
            Write-Host "Setting owner of $ServerPath on $Server"

            $SB ={
                ${$Function:Set-Owner}
                Set-Owner -Path $args[0] -Account $args[1] -Recurse:$args[2] -Confirm:$false
            }

            Invoke-Command -ComputerName $Server -Scriptblock $SB -ArgumentList @($Path,"BUILTIN\Administrators",$true)
        }

        Write-Host "Completed setting crypto folder owner and permissions."
    }

    End {    
    }
}

Function Set-DnsServerLogPermissions {
    <#
        .SYNOPSIS
            The Windows 2012 DNS Server logging criteria must only be configured by the ISSM or individuals appointed by the ISSM.
 
        .DESCRIPTION
            The cmdlet assigns specific access permissions for the DNS server logs The command must be run with Enterprise Admin credentials.
 
        .PARAMETER Forest
            The Forest in which to configure DNS servers.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerLogPermissions
 
            Sets the c:\windows\system32\winevt folder to the default permissions and forces inheritance on all log files.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-AU-000007
            Rule ID
                SV-72983r1
            Vuln ID
                V-58553
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )
    
    Begin {
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Setting Dns Server log permissions."

        $Servers = Get-ForestDnsServers -Forest $Forest -Credential $Credential
        $Path = "$env:SYSTEMROOT\System32\Winevt\Logs"
        $Rules = New-EventLogAccessRuleSet

        foreach ($Server in $Servers)
        {
            $ServerPath = "\\$Server\" + $Path.Replace(":\","$\")
            Set-FileSecurity -Path $ServerPath -AccessRules $Rules -ReplaceAll -ForceChildInheritance
        }

        Write-Host "Completed setting Dns Server log permissions."
    }

    End {    
    }
}

Function Set-DnsServerZoneTransfers {
    <#
        .SYNOPSIS
            The Windows DNS primary server must only send zone transfers to a specific list of secondary name servers.
 
        .DESCRIPTION
            The Set-DnsServerZoneTransfers cmdlet checks each DNS server in the forest to see if it has zone transfers enabled. If it does and are allowed "To any server", then the setting is changed to "Only to servers on the Name Servers tab".
 
        .PARAMETER Forest
            The DNS Forest to configure.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerZoneTransfers
 
            Sets all DNS servers in the forest that have zone transfers enabled to any server to only servers on the Name Servers tab.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-IA-000004
            Rule ID
                SV-73067r1
            Vuln ID
                V-58637
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }
        
        Write-Host "Setting DNS Server Zone Transfer settings."

        $Servers = Get-ForestDnsServers -Forest $Forest -Credential $Credential

        foreach ($Server in $Servers)
        {
            Get-SpecificServerDnsZones -ComputerName $Server -ZoneType Primary | ForEach-Object {
                if ($_.SecureSecondaries -eq "TransferAnyServer")
                {
                    Write-Warning "DNS Server $Server was set to transfer to any server, fixing..."
                    Set-DnsServerPrimaryZone -Name $_.ZoneName -ComputerName $Server -SecureSecondaries TransferToZoneNameServer
                    Write-Host "Done fixing $Server."
                }
            }
        }

        Write-Host "Completed setting DNS Server zone transfer settings"
    }

    End {    
    }
}

Function Remove-DnsServerZoneIPv6LinkLocalAddresses {
    <#
        .SYNOPSIS
            Non-routable IPv6 link-local scope addresses must not be configured in any zone.
 
        .DESCRIPTION
            The Remove-DnsServerZoneIPv6LinkLocalAddresses cmdlet gets all AAAA records in each zone and removes any link-local scope addresses.
 
        .PARAMETER Forest
            The DNS Forest to configure.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Remove-DnsServerZoneIPv6LinkLocalAddresses
             
            Removes all IPv6 link-local addresses from all DNS Zones
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-CM-000026
            Rule ID
                SV-73053r1
            Vuln ID
                V-58623
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Removing IPv6 link local addresses from DNS."

        $Zones = @()

        Get-ForestDNSServers -Forest $Forest -Credential $Credential | ForEach-Object {
            $Zones += Get-DnsServerZone -ComputerName $_ -WarningAction SilentlyContinue | Where-Object {$_.IsReverseLookupZone -eq $false}
        }

        $Zones | Group-Object -Property ZoneName | ForEach-Object {
            $Zone = $_.Name
            Write-Verbose -Message "Processing zone $Zone"

            try
            {
                $Server = Resolve-DnsName -Name $Zone -ErrorAction Stop | Select-Object -ExpandProperty PrimaryServer -ErrorAction SilentlyContinue
                
                if ([System.String]::IsNullOrEmpty($Server)) {
                    Write-Verbose -Message "PrimaryServer attribute was null, using the zone name for the server."
                    $Server = $Zone
                }

                $RemovedRecords = Get-DnsServerResourceRecord -ZoneName $Zone -RRType AAAA -ComputerName $Server | Where-Object {$_.RecordData -match "^FE[89AB].*$"} | Remove-DnsServerResourceRecord -ComputerName $Zone -ZoneName $Zone -Force -Confirm:$false -PassThru
                
                if ($RemovedRecords.Count -gt 0)
                {
                    Write-Warning -Message "Removed the following records on $Server for zone $Zone`:"
                    Write-Warning -Message $RemovedRecords
                }
                else
                {
                    Write-Verbose -Message "No records to remove on $Server for zone $Zone."
                }
            }
            catch [Exception]
            {
                Write-Warning -Message "Could not get records for zone: $Zone`: $($_.ToString())"
            }
        }

        Write-Host "Completed removing IPv6 link local addresses."
    }

    End {        
    }
}

Function Get-InactiveDnsServers {
    <#
        .SYNOPSIS
            The Windows 2012 DNS Servers zone files must have NS records that point to active name servers authoritative for the domain specified in that record.
 
        .DESCRIPTION
            The Get-InactiveDnsServers cmdlet gets all NS records for a forest and then tries to resolve the NS record hostname on that NS server. Any non-active DNS servers are returned.
 
        .PARAMETER Forest
            The DNS Forest to test against.
     
        .EXAMPLE
            PS C:\>Get-InactiveDnsServers
 
            Returns a list of any non-active DNS servers in the forest.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.String[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-CM-000010
            Rule ID
                SV-73023r1
            Vuln ID
                V-58593
            Severity
                CAT I
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty
    )

    Begin {    
    }

    Process
    {
        $InactiveServers = @()
        Write-Host "Getting all inactive DNS servers."

        Get-ForestDnsServers -Forest $Forest | ForEach-Object {
            $Server = $_
            try
            {
                Resolve-DnsName -Name $_ -Server $_ -ErrorAction Stop | Out-Null
            }
            catch [Exception]
            {
                $InactiveServers += $Server
            }
        }

        Write-Output -InputObject $InactiveServers
    }

    End {    
    }
}

Function Set-DnsServerDisableRootHints {
    <#
        .SYNOPSIS
            Forwarders on an authoritative Windows 2012 DNS Server, if enabled for external resolution, must only forward to either an internal, non-AD-integrated DNS server or to the DoD Enterprise Recursive Services (ERS).
 
        .DESCRIPTION
            The Set-DnsServerDisableRootHints cmdlet removes all root hints and disables using root hints on each DNS server in the forest. The command must be run with Enterprise Admin credentials.
 
        .PARAMETER Forest
            The DNS Forest to set the root hints settings on.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerDisableRootHints
 
            Disables root hints on all servers and removes root hints on all servers.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-CM-000004
                WDNS-CM-000022
            Rule ID
                SV-73011r1
                SV-73045r1
            Vuln ID
                V-58581
                V-58615
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Disabling all DNS server root hints."

        $Servers = Get-ForestDnsServers -Forest $Forest -Credential $Credential

        foreach ($Server in $Servers)
        {
            Get-DnsServerRootHint -ComputerName $Server | Remove-DnsServerRootHint -ComputerName $Server -Force -Confirm:$false
            Invoke-Command -ComputerName $Server -ScriptBlock {Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\DNS\Parameters" -Name "IsSlave" -Value 1 } -Credential $Credential
        }
    }

    End {
    }
}

Function Set-DnsServerRecursionAndForwarders {
    <#
        .SYNOPSIS
            The Windows 2012 DNS Server must prohibit recursion on authoritative name servers for which forwarders have not been configured for external queries.
 
        .DESCRIPTION
            The Set-DnsServerRecursionAndForwarders cmdlet checks to see if forwarders are disabled, if they are it also disables recursion. The command must be run with Enterprise Admin credentials.
 
        .PARAMETER Forest
            The DNS Forest to set the recursion setting on.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerRecursionAndForwarders
 
            Disables recursion on all DNS servers without forwarders
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-CM-000003
            Rule ID
                SV-73009r1
            Vuln ID
                V-58579
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {
    }

    Process 
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Setting DNS recursion and forwarders settings."

        $Servers = Get-ForestDnsServers -Forest $Forest -Credential $Credential

        foreach ($Server in $Servers)
        {
            $Forwarders = Get-DnsServerForwarder -ComputerName $Server

            if ($Forwarders.IPAddress.Count -eq 0)
            {
                Set-DnsServerRecursion -ComputerName $Server -Enable $false
            }
        }

        Write-Host "Completed setting recursions and forwarders."
    }

    End {    
    }
}

Function Set-DnsServerZoneSecureUpdates {
    <#
        .SYNOPSIS
            The Windows 2012 DNS Server must restrict incoming dynamic update requests to known clients.
 
        .DESCRIPTION
            The Set-DnsServerZoneSecureUpdates cmdlet turns on secure updates only for all DNS servers in the forest. The command must be run with Enterprise Admin credentials.
 
        .PARAMETER Forest
            The DNS Forest to set secure updates on.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerZoneSecureUpdates
 
            Enforces secure updates on each primary zone in the current user forest.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-AC-000001
                WDNS-IA-000001
            Rule ID
                SV-72667r1
                SV-73061r1
            Vuln ID
                V-58237
                V-58631
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {
    }

    Process 
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Setting secure updates on Dns Servers."

        Get-ForestDnsZones -Forest $Forest -ZoneType Primary -DsIntegration DsIntegrated -Credential $Credential | ForEach-Object {
            Write-Host "Configuring secure dynamic updates on $($_.ZoneName)"
            
            $Zone = $_.ZoneName
            try
            {
                $Server = Resolve-DnsName -Name $_.ZoneName -ErrorAction Stop | Select-Object -ExpandProperty PrimaryServer -ErrorAction SilentlyContinue
                
                if ([System.String]::IsNullOrEmpty($Server)) {
                    Write-Verbose -Message "PrimaryServer attribute was null, using the zone name for the server."
                    $Server = $_.ZoneName
                }

                Set-DnsServerPrimaryZone -Name $_.ZoneName -DynamicUpdate Secure -ComputerName $Server
            }
            catch [Exception]
            {
                Write-Warning "Error setting secure dynamic updates on $Zone`: $($_.ToString())"
            }
        }

        Write-Host "Completed setting secure updates."
    }

    End {        
    }
}

Function Set-DnsServerLogging {
    <#
        .SYNOPSIS
            The Windows 2012 DNS Server must be configured to record, and make available to authorized personnel, who added/modified/deleted DNS zone information.
 
        .DESCRIPTION
            The cmdlet turns on logging and log rollover on every DNS server in the forest. The command must be run with Enterprise Admin credentials.
 
        .PARAMETER Forest
            The DNS Forest to set secure updates on.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerLogging
 
            Enables logging on every DNS server in the current user's forest.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-AU-000001
                WDNS-AU-000005
                WDNS-AU-000006
                WDNS-AU-000007
                WDNS-AU-000008
                WDNS-AU-000010
                WDNS-AU-000011
                WDNS-AU-000012
                WDNS-AU-000013
                WDNS-AU-000014
                WDNS-AU-000015
                WDNS-SI-000009
            Rule ID
                SV-72973r1
                SV-72979r1
                SV-72981r1
                SV-72983r1
                SV-72985r1
                SV-72991r1
                SV-72993r1
                SV-72995r1
                SV-72997r1
                SV-72999r1
                SV-73001r1
                SV-73149r1
            Vuln ID
                V-58543
                V-58549
                V-58551
                V-58553
                V-58555
                V-58561
                V-58563
                V-58565
                V-58567
                V-58569
                V-58571
                V-58719
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Setting Dns Server logging."

        Get-ForestDnsServers -Forest $Forest -Credential $Credential | ForEach-Object {
            Set-DnsServerDiagnostics -ComputerName $_ -All $true
            Set-DnsServerDiagnostics -EnableLogFileRollover $true
        }

        Write-Host "Completed setting Dns Server logging."
    }

    End {        
    }
}

Function Set-DnsServerVersionQuery {
    <#
        .SYNOPSIS
            The DNS Name Server software must be configured to refuse queries for its version information.
 
        .DESCRIPTION
            The Set-DnsServerVersionQuery cmdlet disables returning version information from a DNS query on all DNS servers in the Forest. The command must be run with Enterprise Admin credentials.
 
        .PARAMETER Forest
            The forest to configure.
 
        .PARAMETER Credential
            The credential to use, requires Enterprise Admin rights.
 
        .EXAMPLE
            PS C:\>Set-DnsServerVersionQuery
 
            Disables the return of version information from DNS queries.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            None
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
 
        .FUNCTIONALITY
            STIG
                Microsoft Windows 2012 Server DNS V1R2
            STIG ID
                WDNS-SI-000003
            Rule ID
                SV-73167r1
            Vuln ID
                V-58737
            Severity
                CAT II
    #>


    Param(
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty  
    )

    Begin {        
    }

    Process
    {
        if (!(Test-IsEnterpriseAdmin -Credential $Credential))
        {
            Write-Error "The cmdlet must be run with Enterprise Admin credentials."
            Exit 1
        }

        Write-Host "Setting Dns Server version query settings."

        Get-ForestDnsServers -Forest $Forest -Credential $Credential |  ForEach-Object {
            $Server = $_

            try
            {
                Invoke-Command -ComputerName $_ -ScriptBlock {Set-ItemProperty -Path "HKLM:\\SYSTEM\\CurrentControlSet\\Services\\DNS\\Parameters" -Name "EnableVersionQuery" -Value 0} -Credential $Credential
            }
            catch [Exception] 
            {
                Write-Host "Error on $Server`: $($_.ToString())"
            }
        }

        Write-Host "Completed setting Dns Server version query settings."
    }

    End {
    }
}

Function Get-ForestDnsServers {
    <#
        .SYNOPSIS
            Finds all of the DNS servers for the current forest.
 
        .DESCRIPTION
            Finds all of the DNS servers registered as NS servers in the forest and returns an array of DNS names.
 
        .PARAMETER Forest
            Specify the forest to get the name servers from.
 
        .PARAMETER Credential
            The credential to use to query for the current AD Forest object.
 
        .EXAMPLE
            PS C:\>Get-ForestDnsServers
 
            Gets all of the DNS server in the current forest.
 
        .INPUTS
            System.String
 
        .OUTPUTS
            System.String[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
    #>


    Param (
        [Parameter(Position=0,ValueFromPipeline=$true)]
        [string]$Forest = [System.String]::Empty,

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin 
    {
        try {
            Import-Module ActiveDirectory
        }
        catch [Exception] {
            Write-Warning "This cmdlet requires the Active Directory module."
            Exit 1
        }
    }

    Process
    {
        if ($Forest -eq [System.String]::Empty)
        {
            if ($Credential -ne [PSCredential]::Empty)
            {
                $RootDomain = (Get-ADForest -Current $Credential.UserName -Credential $Credential).RootDomain
            }
            else
            {
                $RootDomain = (Get-ADForest -Current LoggedOnUser).RootDomain
            }
        }
        else
        {
            $RootDomain = $Forest
        }

        Write-Output -InputObject (Resolve-DnsName -Name $RootDomain -Type NS | Where-Object {![System.String]::IsNullOrEmpty($_.NameHost)} | Select-Object -ExpandProperty NameHost)
    }

    End {
    }
}

Function Get-ForestDnsZones {
    <#
        .SYNOPSIS
            Get all of the forward lookup zones in a forest.
 
        .DESCRIPTION
            The Get-ForestDnsZones cmdlet gets all of the primary forward lookup zones for a forest.
 
        .PARAMETER Forest
            The DNS Forest to search.
 
        .PARAMETER ZoneType
            Select from All, Primary, Seconday, or Stub. Defaults to All.
 
        .PARAMETER DsIntegration
            Select from All, DsIntegrated, or NonDsIntegrated. Defaults to All.
 
        .PARAMETER LookupType
            Select from All, Forward, or Reverse. Defaults to All.
 
        .PARAMETER Credential
            The credential to use.
 
        .EXAMPLE
            PS C:\>Get-ForestDnsZones
 
            Gets all of the dns zones in the current user's forest.
 
        .EXAMPLE
            PS C:\>Get-ForestDnzZones -ZoneType Primary -DsIntegration DsIntegrated -LookupType Forward
 
            Gets all primary, Active Directory integrated, forward lookup zones in the current user forest.
 
        .INPUTS
            None
 
        .OUTPUTS
            PSObject[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
    #>


    Param(
        [Parameter(Position=0)]
        [string]$Forest = [System.String]::Empty,

        [Parameter(Position=1)]
        [ValidateSet("All","Primary","Secondary","Stub")]
        [string]$ZoneType = "All",

        [Parameter(Position=2)]
        [ValidateSet("All","DsIntegrated","NonDsIntegrated")]
        [string]$DsIntegration = "All",

        [Parameter(Position=3)]
        [ValidateSet("All","Forward","Reverse")]
        [string]$LookupType = "All",

        [Parameter()]
        [ValidateNotNull()]
        [System.Management.Automation.PSCredential]
        [System.Management.Automation.Credential()]
        $Credential = [System.Management.Automation.PSCredential]::Empty 
    )

    Begin {
    }

    Process
    {
        $Zones = @()

        Get-ForestDNSServers -Forest $Forest -Credential $Credential | ForEach-Object {
            $Zones += Get-SpecificServerDnsZones -ComputerName $_ -ZoneType $ZoneType -DsIntegration $DsIntegration -LookupType $LookupType | Where-Object {-not $_.ZoneName.StartsWith("_")}
        }

        $Zones | Group-Object -Property ZoneName | Select-Object -Property Group | ForEach-Object {
            Write-Output -InputObject ($_.Group | Select-Object -First 1)
        }
    }
    
    End {        
    }
}

Function Get-SpecificServerDnsZones {
    <#
        .SYNOPSIS
            Get all of the forward lookup zones in a forest.
 
        .DESCRIPTION
            The Get-ForestDnsZones cmdlet gets all of the primary forward lookup zones for a forest.
 
        .PARAMETER Forest
            The DNS Forest to search.
 
        .PARAMETER ZoneType
            Select from All, Primary, Seconday, or Stub. Defaults to All.
 
        .PARAMETER DsIntegration
            Select from All, DsIntegrated, or NonDsIntegrated. Defaults to All.
 
        .PARAMETER LookupType
            Select from All, Forward, or Reverse. Defaults to All.
 
        .EXAMPLE
            PS C:\>Get-SpecificServerDnsZones
 
            Gets all of the dns zones on the localhost.
 
        .EXAMPLE
            PS C:\>Get-SpecificServerDnsZones -ComputerName server01
 
            Gets all of the dns zones on server01.
 
        .EXAMPLE
            PS C:\>Get-SpecificServerDnsZones -ComputerName server01 -ZoneType Primary -DsIntegration DsIntegrated -LookupType Forward
 
            Gets all primary, Active Directory integrated, forward lookup zones on server01.
 
        .INPUTS
            None
 
        .OUTPUTS
            PSObject[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
    #>


    Param(
        [Parameter(Position=0)]
        [string]$ComputerName ="localhost",

        [Parameter(Position=1)]
        [ValidateSet("All","Primary","Secondary","Stub")]
        [string]$ZoneType = "All",

        [Parameter(Position=2)]
        [ValidateSet("All","DsIntegrated","NonDsIntegrated")]
        [string]$DsIntegration = "All",

        [Parameter(Position=3)]
        [ValidateSet("All","Forward","Reverse")]
        [string]$LookupType = "All"
    )

    Begin {}

    Process
    {
        switch (($ZoneType + $DsIntegration + $LookupType))
        {
            default {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue)}
            "AllAllAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue)}
            "AllAllForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.IsReverseLookupzone -eq $false})}
            "AllAllReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.IsReverseLookupzone -eq $true})}
            "AllDsIntegratedAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.IsDsIntegrated -eq $true})}
            "AllDsIntegratedForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $false})}
            "AllDsIntegratedReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.IsDsIntegrated -eq $true -and $_.IsReverseLookupzone -eq $true})}
            "AllNonDsIntegratedAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.IsDsIntegrated -eq $false})}
            "AllNonDsIntegratedForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.IsDsIntegrated -eq $false -and $_.IsReverseLookupZone -eq $false})}
            "AllNonDsIntegratedReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.IsDsIntegrated -eq $false -and $_.IsReverseLookupzone -eq $true})}

            "PrimaryAllAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary"})}
            "PrimaryAllForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary" -and $_.IsReverseLookup -eq $false})}
            "PrimaryAllReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary" -and $_.IsReverseLookup -eq $true})}
            "PrimaryDsIntegratedAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary" -and $_.IsDsIntegrated -eq $true})}
            "PrimaryDsIntegratedForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $false})}
            "PrimaryDsIntegratedReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $true})}
            "PrimaryNonDsIntegratedAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary" -and $_.IsDsIntegrated -eq $false})}
            "PrimaryNonDsIntegratedForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary" -and $_.IsDsIntegrated -eq $false -and $_.IsReverseLookupZone -eq $false})}
            "PrimaryNonDsIntegratedReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Primary" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $true})}

            "SecondaryAllAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary"})}
            "SecondaryAllForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary" -and $_.IsReverseLookup -eq $false})}
            "SecondaryAllReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary" -and $_.IsReverseLookup -eq $true})}
            "SecondaryDsIntegratedAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary" -and $_.IsDsIntegrated -eq $true})}
            "SecondaryDsIntegratedForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $false})}
            "SecondaryDsIntegratedReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $true})}
            "SecondaryNonDsIntegratedAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary" -and $_.IsDsIntegrated -eq $false})}
            "SecondaryNonDsIntegratedForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary" -and $_.IsDsIntegrated -eq $false -and $_.IsReverseLookupZone -eq $false})}
            "SecondaryNonDsIntegratedReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Secondary" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $true})}

            "StubAllAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub"})}
            "StubAllForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub" -and $_.IsReverseLookup -eq $false})}
            "StubAllReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub" -and $_.IsReverseLookup -eq $true})}
            "StubDsIntegratedAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub" -and $_.IsDsIntegrated -eq $true})}
            "StubDsIntegratedForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $false})}
            "StubDsIntegratedReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $true})}
            "StubNonDsIntegratedAll" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub" -and $_.IsDsIntegrated -eq $false})}
            "StubNonDsIntegratedForward" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub" -and $_.IsDsIntegrated -eq $false -and $_.IsReverseLookupZone -eq $false})}
            "StubNonDsIntegratedReverse" {Write-Output (Get-DnsServerZone -ComputerName $ComputerName -WarningAction SilentlyContinue | Where-Object {$_.ZoneType -eq "Stub" -and $_.IsDsIntegrated -eq $true -and $_.IsReverseLookupZone -eq $true})}
        }
    }
    
    End {}
}

Function Set-ForestDnsServerScavenging {
    <#
        .SYNOPSIS
            Sets DNS record scavenging for all zones in the Forest.
 
        .DESCRIPTION
            Creates the File Access Rules for EventLog, System, and Administrators.
 
        .PARAMETER RefreshInterval
            Specifies the refresh interval as a TimeSpan object. During this interval, a DNS server can refresh a resource record that has a non-zero time stamp. Zones on the server inherit this value automatically.
             
            If a DNS server does not refresh a resource record that has a non-zero time stamp, the DNS server can remove that record during the next scavenging.
 
            Do not select a value smaller than the longest refresh period of a resource record registered in the zone.
 
            The minimum value is 0. The maximum value is 8760 hours (seven days). The default value is 7 days.
 
        .PARAMETER ScavengingInterval
            Specifies a length of time as a TimeSpan object. ScavengingInterval determines whether the scavenging feature for the DNS server is enabled and sets the number of hours between scavenging cycles.
 
            The default setting is 7. A setting greater than 0 enables scavenging for the server and sets the number of days, hours, minutes, and seconds (formatted as dd.hh:mm:ss) between scavenging cycles. The minimum value is 0. The maximum value is 365.00:00:00 (1 year).
 
        .EXAMPLE
            PS C:\>Set-ForestDnsServerScavenging
 
            Enables stale record scavenging
 
        .INPUTS
            System.TimeSpan, System.TimeSpan
 
        .OUTPUTS
            Microsoft.Management.Infrastructure.CimInstance#DnsServerScavenging[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
    #>

    [CmdletBinding()]
    Param(
        [Parameter(Position=0)]
        [System.TimeSpan]$RefreshInterval = [System.TimeSpan]::FromDays(7),

        [Parameter(Position=1)]
        [System.TimeSpan]$ScavengingInterval = [System.TimeSpan]::FromDays(7),

        [Parameter()]
        [switch]$PassThru
    )

    Begin {        
    }

    Process {
        $Forest = Get-ADForest

        Write-Host "Setting DNS Server Scavenging."

        foreach ($Domain in $Forest.Domains)
        {
            $Context = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext([System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain, $Domain)
            $Servers = [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($Context)
            
            foreach ($Server in $Servers)
            {
                try {
                    $Scavenging = Set-DnsServerScavenging -ScavengingState $true -ApplyOnAllZones -ComputerName $Server.Name -RefreshInterval ([System.TimeSpan]::FromDays(7)) -ScavengingInterval ([System.TimeSpan]::FromDays(7)) -PassThru

                    if ($PassThru) {
                        Write-Output $Scavenging
                    }
                }
                catch [Exception] {
                    Write-Warning $_.Exception.Message
                }
            }
        } 
    }
    
    End {
    }
}

Function New-EventLogAccessRuleSet {
    <#
        .SYNOPSIS
            Creates the File Access Rules for EventLog, System, and Administrators.
 
        .DESCRIPTION
            Creates the File Access Rules for EventLog, System, and Administrators.
 
        .EXAMPLE
            PS C:\>New-EventLogAccessRuleSet
 
            Creates the rule set.
 
        .INPUTS
            None
 
        .OUTPUTS
            System.Security.AccessControl.FileSystemAccessRule[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
    #>

    [CmdletBinding()]
    Param(
    )

    Begin {
    }

    Process {
        # NT Service\EventLog = S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122
        [System.Security.Principal.SecurityIdentifier]$NTServiceEventLogSid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122")
        $Administrators = New-Object Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid, $null)
        $EventLog = New-Object Security.Principal.SecurityIdentifier($NTServiceEventLogSid)
        $System = New-Object  Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::LocalSystemSid, $null)

        $AdministratorAce = New-Object System.Security.AccessControl.FileSystemAccessRule($Administrators, 
            [System.Security.AccessControl.FileSystemRights]::FullControl, 
            @([System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.InheritanceFlags]::ObjectInherit),
            [System.Security.AccessControl.PropagationFlags]::None,
            [System.Security.AccessControl.AccessControlType]::Allow)

        $EventLogAce = New-Object System.Security.AccessControl.FileSystemAccessRule($EventLog, 
            [System.Security.AccessControl.FileSystemRights]::FullControl, 
            @([System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.InheritanceFlags]::ObjectInherit),
            [System.Security.AccessControl.PropagationFlags]::None,
            [System.Security.AccessControl.AccessControlType]::Allow)

        $SystemAce = New-Object System.Security.AccessControl.FileSystemAccessRule($System, 
            [System.Security.AccessControl.FileSystemRights]::FullControl, 
            @([System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.InheritanceFlags]::ObjectInherit),
            [System.Security.AccessControl.PropagationFlags]::None,
            [System.Security.AccessControl.AccessControlType]::Allow)


        [System.Security.AccessControl.FileSystemAccessRule[]]$Rules = @($AdministratorAce, $EventLogAce, $SystemAce)

        Write-Output -InputObject $Rules
    }

    End {    
    }
}

Function New-CryptoFolderAccessRuleSet {
    <#
        .SYNOPSIS
            Creates the File Access Rules for System and Administrators.
 
        .DESCRIPTION
            Creates the File Access Rules for System and Administrators.
 
        .EXAMPLE
            PS C:\>New-CryptoFolderAccessRuleSet
 
            Creates the rule set.
        .INPUTS
            None
 
        .OUTPUTS
            System.Security.AccessControl.FileSystemAccessRule[]
 
        .NOTES
            AUTHOR: Michael Haken
            LAST UPDATE: 2/28/2016
    #>

    [CmdletBinding()]
    Param()

    Begin {
    }

    Process {
        [System.Security.Principal.SecurityIdentifier]$NTServiceEventLogSid = New-Object System.Security.Principal.SecurityIdentifier("S-1-5-80-880578595-1860270145-482643319-2788375705-1540778122")
        $Administrators = New-Object Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::BuiltinAdministratorsSid, $null)
        $System = New-Object  Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::LocalSystemSid, $null)

        $AdministratorAce = New-Object System.Security.AccessControl.FileSystemAccessRule($Administrators, 
            [System.Security.AccessControl.FileSystemRights]::FullControl, 
            @([System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.InheritanceFlags]::ObjectInherit),
            [System.Security.AccessControl.PropagationFlags]::None,
            [System.Security.AccessControl.AccessControlType]::Allow)

        $SystemAce = New-Object System.Security.AccessControl.FileSystemAccessRule($System, 
            [System.Security.AccessControl.FileSystemRights]::FullControl, 
            @([System.Security.AccessControl.InheritanceFlags]::ContainerInherit, [System.Security.AccessControl.InheritanceFlags]::ObjectInherit),
            [System.Security.AccessControl.PropagationFlags]::None,
            [System.Security.AccessControl.AccessControlType]::Allow)

        [System.Security.AccessControl.FileSystemAccessRule[]] $Rules = @($AdministratorAce, $SystemAce)

        Write-Output -InputObject $Rules
    }

    End {        
    }
}