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, System.Management.Automation.SwitchParameter, System.Management.Automation.PSCredential .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/28/2016 #> Param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [switch]$RemoveProhibitedRecords = $false, [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } if (!(Test-IsEnterpriseAdmin -Credential $Credential)) { Write-Error "The cmdlet must be run with Enterprise Admin credentials." Exit 1 } Write-Host "Starting DNS STIG" } Process { if ($RemoveProhibitedRecords) { Remove-DnsServerProhibitedRecords -Forest $Forest -Credential $Credential } else { Get-DnsServerProhibitedRecords -Forest $Forest -Credential $Credential | ForEach-Object { Write-Warning ($_.Record | Out-String) } } Get-DnsServersRunningIPv6 -Forest $Forest -Credential $Credential | ForEach-Object { Write-Warning $_.ComputerName } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { 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 Remove-DnsServerResourceRecord -ZoneName $_.ZoneName -ComputerName $Server -InputObject $_.Record -Force -Confirm:$false } catch [Exception] { Write-Warning "Error removing record $Record`: $($_.ToString())" } } } End { Write-Host "Removing prohibited entries complete." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } Write-Host "Getting DNS Server prohibited entries." } Process { $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 $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())" } } } End { Write-Output $BadRecords } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } Write-Host "Getting DNS Servers running IPv6 and not hosting AAAA records." } Process { #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-RegistryEntry} -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} } } } } } } End { if ($BadZones.Count -gt 0) { Write-Warning "Found servers without AAAA records that have IPv6 enabled." } Write-Output $BadZones } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { $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 } } } } End { Write-Host "Completed setting secondary server notification settings." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } if (!(Test-IsEnterpriseAdmin -Credential $Credential)) { Write-Error "The cmdlet must be run with Enterprise Admin credentials." Exit 1 } Write-Host "Setting WINS server settings." } Process { $Servers = Get-ForestDnsServers -Forest $Forest foreach ($Server in $Servers) { Get-SpecificServerDnsZones -ComputerName $Server -ZoneType Primary -LookupType Forward | ForEach-Object { #TODO: Set WINS Setting } } } End { Write-Host "Completed WINS server settings." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { $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-FilePermissions -Path $ServerPath -Rules $Rules -Replace -ForceInheritance Write-Host "Setting folder owner on $Server" Invoke-Command -ComputerName $Server -Scriptblock ${function:Set-Owner} -ArgumentList $Path,"BUILTIN\Administrators",$true } } End { Write-Host "Completed setting crypto folder owner and permissions." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { $Servers = Get-ForestDnsServers -Forest $Forest -Credential $Credential $Path = "$env:SYSTEMROOT\System32\Winevt\Logs" $Rules = New-EventLogAccessRuleSet foreach ($Server in $Servers) { Write-Host $Server $ServerPath = "\\$Server\" + $Path.Replace(":\","$\") Set-FilePermissions -Path $ServerPath -Rules $Rules -Replace -ForceInheritance } } End { Write-Host "Completed setting Dns Server log permissions." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { $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." } } } } End { Write-Host "Completed setting DNS Server zone transfer settings" } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter()] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { $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 $Zone try { $Server = Resolve-DnsName -Name $Zone -ErrorAction Stop | Select-Object -ExpandProperty PrimaryServer $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 "Removed the following records:" Write-Warning $RemovedRecords } else { Write-Host "No records to remove." } } catch [Exception] { Write-Warning "Could not get records for zone: $Zone`: $($_.ToString())" } } } End { Write-Host "Completed removing IPv6 link local addresses." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty ) Begin { $InactiveServers = @() Write-Host "Getting all inactive DNS servers." } Process { Get-ForestDnsServers -Forest $Forest | ForEach-Object { $Server = $_ try { Resolve-DnsName -Name $_ -Server $_ -ErrorAction Stop | Out-Null } catch [Exception] { $InactiveServers += $Server } } } End { Write-Output $InactiveServers } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { $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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { $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 } } } End { Write-Host "Completed setting recursions and forwarders." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { 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 Set-DnsServerPrimaryZone -Name $_.ZoneName -DynamicUpdate Secure -ComputerName $Server } catch [Exception] { Write-Warning "Error setting secure dynamic updates on $Zone`: $($_.ToString())" } } } End { Write-Host "Completed setting secure updates." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } if (!(Test-IsEnterpriseAdmin -Credential $Credential)) { Write-Error "The cmdlet must be run with Enterprise Admin credentials." Exit 1 } Write-Host "Setting Dns Server logging." } Process { Get-ForestDnsServers -Forest $Forest -Credential $Credential | ForEach-Object { Write-Host $_ Set-DnsServerDiagnostics -ComputerName $_ -All $true Set-DnsServerDiagnostics -EnableLogFileRollover $true } } End { Write-Host "Completed setting Dns Server logging." } } 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, System.Management.Automation.PSCredential .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,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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." } Process { Get-ForestDnsServers -Forest $Forest -Credential $Credential | ForEach-Object { $Server = $_ Write-Host $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())" } } } End { Write-Host "Completed setting Dns Server version query settings." } } 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, System.Management.Automation.PSCredential .OUTPUTS System.String[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/28/2016 #> Param ( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } 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 (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 System.String, System.String, System.String, System.String, System.Management.Automation.PSCredential .OUTPUTS PSObject[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/28/2016 #> Param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [string]$Forest = [System.String]::Empty, [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [ValidateSet("All","Primary","Secondary","Stub")] [string]$ZoneType = "All", [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [ValidateSet("All","DsIntegrated","NonDsIntegrated")] [string]$DsIntegration = "All", [Parameter(Position=3,ValueFromPipelineByPropertyName=$true)] [ValidateSet("All","Forward","Reverse")] [string]$LookupType = "All", [Parameter(Position=4,ValueFromPipelineByPropertyName=$true)] [System.Management.Automation.PSCredential] [System.Management.Automation.Credential()] $Credential = [System.Management.Automation.PSCredential]::Empty ) Begin { if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } } 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("_")} } } End { $Zones | Group-Object -Property ZoneName | Select-Object -Property Group | ForEach-Object { Write-Output ($_.Group | Select-Object -First 1) } } } 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 System.String, System.String, System.String, System.String .OUTPUTS PSObject[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/28/2016 #> Param( [Parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [string]$ComputerName ="localhost", [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [ValidateSet("All","Primary","Secondary","Stub")] [string]$ZoneType = "All", [Parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [ValidateSet("All","DsIntegrated","NonDsIntegrated")] [string]$DsIntegration = "All", [Parameter(Position=3,ValueFromPipelineByPropertyName=$true)] [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, System.Management.Automation.SwitchParameter .OUTPUTS Microsoft.Management.Infrastructure.CimInstance#DnsServerScavenging[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/28/2016 #> [CmdletBinding()] Param( [Parameter(Position=0,ValueFromPipelineByPropertyName=$true)] [System.TimeSpan]$RefreshInterval = [System.TimeSpan]::FromDays(7), [Parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [System.TimeSpan]$ScavengingInterval = [System.TimeSpan]::FromDays(7), [Parameter(Position=2)] [switch]$PassThru = $false ) Begin { $Forest = Get-ADForest Write-Host "Setting DNS Server Scavenging." } Process { foreach ($Domain in $Forest.Domains) { Write-Host $Domain $Context = New-Object System.DirectoryServices.ActiveDirectory.DirectoryContext([System.DirectoryServices.ActiveDirectory.DirectoryContextType]::Domain, $Domain) $Servers = [System.DirectoryServices.ActiveDirectory.DomainController]::FindAll($Context) foreach ($Server in $Servers) { Write-Host $Server.Name 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) } End { Write-Output $Rules } } 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) } End { Write-Output $Rules } } Function Set-FilePermissions { <# .SYNOPSIS Sets permissions on a file or directory. .DESCRIPTION Will set permissions on file or directory with the provided rule set. .PARAMETER Path The path to the file to set permissions on. .PARAMETER Rules An array of File Access Rules to apply to the path. .PARAMETER Replace Indictates if all permissions on the path should be replaced with these. Otherwise the specified access rules will just be added to the target. .PARAMETER ForceInheritance Indicates if all permissions of child items should have their permissions replaced with these if the target is a directory. .EXAMPLE PS C:\>Set-Permissions -Path "c:\test.txt" -ComputerName -Rules $Rules Creates the rule set on the test.txt file. .INPUTS System.String, System.Security.AccessControl.FileSystemAccessRule[], System.Management.Automation.SwitchParameter, System.Management.Automation.SwitchParameter .OUTPUTS None .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/28/2016 #> Param ( [Parameter(Position=0,Mandatory=$true,ValueFromPipeLineByPropertyName=$true)] [string]$Path, [Parameter(Position=1,Mandatory=$true,ValueFromPipeLineByPropertyName=$true)] [System.Security.AccessControl.FileSystemAccessRule[]]$Rules, [Parameter(Position=2,ValueFromPipeLineByPropertyName=$true)] [switch]$Replace = $false, [Parameter(Position=3,ValueFromPipeLineByPropertyName=$true)] [switch]$ForceInheritance = $false ) Begin { Write-Host "Setting permissions on $Path" } Process { try { $Acl = Get-Acl -Path $Path if ($Acl -ne $null) { #Should the permissions be replaced if($Replace) { $OldAcls = $Acl.Access foreach ($Rule in $OldAcls) { $Acl.RemoveAccessRule($Rule) | Out-Null } } #Only remove the permissions for principals we're updating else { $OldAcls = $Acl.Access | Where-Object {$Rules.IdentityReference -eq $_.IdentityReference.Translate([System.Security.Principal.SecurityIdentifier])} foreach ($Rule in $OldAcls) { $Acl.RemoveAccessRule($Rule) | Out-Null } } foreach ($Rule in $Rules) { $Acl.AddAccessRule($Rule) | Out-Null } Set-Acl -Path $Path -AclObject $Acl #If child permissions should be forced to inherit if ($ForceInheritance) { Get-ChildItem -Path $Path -Recurse -Force | ForEach-Object { $ChildAcl = Get-Acl -Path $_.FullName $ChildPath = $_.FullName Write-Host "Forcing inheritance on $ChildPath" foreach ($ChildRule in $ChildAcl.Access) { try { $ChildAcl.RemoveAccessRule($ChildRule) | Out-Null } catch [Exception] { Write-Warning "Error removing ACL from $ChildPath`: $($_.ToString())" } } $ChildAcl.SetAccessRuleProtection($false,$false) Set-Acl -Path $_.FullName -AclObject $ChildAcl | Out-Null } } Write-Host $env:COMPUTERNAME -ForegroundColor DarkRed -BackgroundColor White Write-Host $Path -ForegroundColor DarkRed -BackgroundColor White $Acl.Access } else { Write-Warning "Could not retrieve the ACL for $Path" } } catch [System.Exception] { Write-Warning $_.Exception.Message } } End {} } Function Set-Owner { <# .SYNOPSIS Changes owner of a file or folder to another user or group. .DESCRIPTION Changes owner of a file or folder to another user or group. .PARAMETER Path The folder or file that will have the owner changed. .PARAMETER Account Optional parameter to change owner of a file or folder to specified account. Default value is 'Builtin\Administrators' .PARAMETER Recurse Recursively set ownership on subfolders and files beneath given folder. .EXAMPLE PS C:\>Set-Owner -Path C:\temp\test.txt Changes the owner of test.txt to Builtin\Administrators .EXAMPLE PS C:\>Set-Owner -Path C:\temp\test.txt -Account 'Domain\bprox Changes the owner of test.txt to Domain\bprox .EXAMPLE PS C:\>Set-Owner -Path C:\temp -Recurse Changes the owner of all files and folders under C:\Temp to Builtin\Administrators .EXAMPLE PS C:\>Get-ChildItem C:\Temp | Set-Owner -Recurse -Account 'Domain\Administrator' Changes the owner of all files and folders under C:\Temp to Domain\Administrator .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/28/2016 #> [CmdletBinding(SupportsShouldProcess = $true)] Param ( [parameter(Position=0,ValueFromPipeline=$true,ValueFromPipelineByPropertyName=$true)] [Alias("FullName")] [string[]]$Path, [parameter(Position=1,ValueFromPipelineByPropertyName=$true)] [string]$Account = 'BUILTIN\Administrators', [parameter(Position=2,ValueFromPipelineByPropertyName=$true)] [switch]$Recurse ) Begin { #Prevent Confirmation on each Write-Debug command when using -Debug If ($PSBoundParameters['Debug']) { $DebugPreference = 'Continue' } Try { [void][TokenAdjuster] } Catch { $AdjustTokenPrivileges = @" using System; using System.Runtime.InteropServices; public class TokenAdjuster { [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall, ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen); [DllImport("kernel32.dll", ExactSpelling = true)] internal static extern IntPtr GetCurrentProcess(); [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)] internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok); [DllImport("advapi32.dll", SetLastError = true)] internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid); [StructLayout(LayoutKind.Sequential, Pack = 1)] internal struct TokPriv1Luid { public int Count; public long Luid; public int Attr; } internal const int SE_PRIVILEGE_DISABLED = 0x00000000; internal const int SE_PRIVILEGE_ENABLED = 0x00000002; internal const int TOKEN_QUERY = 0x00000008; internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; public static bool AddPrivilege(string privilege) { try { bool retVal; TokPriv1Luid tp; IntPtr hproc = GetCurrentProcess(); IntPtr htok = IntPtr.Zero; retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); tp.Count = 1; tp.Luid = 0; tp.Attr = SE_PRIVILEGE_ENABLED; retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); return retVal; } catch (Exception ex) { throw ex; } } public static bool RemovePrivilege(string privilege) { try { bool retVal; TokPriv1Luid tp; IntPtr hproc = GetCurrentProcess(); IntPtr htok = IntPtr.Zero; retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok); tp.Count = 1; tp.Luid = 0; tp.Attr = SE_PRIVILEGE_DISABLED; retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid); retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero); return retVal; } catch (Exception ex) { throw ex; } } } "@ Add-Type $AdjustTokenPrivileges } #Activate necessary admin privileges to make changes without NTFS perms [void][TokenAdjuster]::AddPrivilege("SeRestorePrivilege") #Necessary to set Owner Permissions [void][TokenAdjuster]::AddPrivilege("SeBackupPrivilege") #Necessary to bypass Traverse Checking [void][TokenAdjuster]::AddPrivilege("SeTakeOwnershipPrivilege") #Necessary to override FilePermissions } Process { ForEach ($Item in $Path) { Write-Verbose "FullName: $Item" #The ACL objects do not like being used more than once, so re-create them on the Process block $DirOwner = New-Object System.Security.AccessControl.DirectorySecurity $DirOwner.SetOwner([System.Security.Principal.NTAccount]$Account) $FileOwner = New-Object System.Security.AccessControl.FileSecurity $FileOwner.SetOwner([System.Security.Principal.NTAccount]$Account) $DirAdminAcl = New-Object System.Security.AccessControl.DirectorySecurity $FileAdminAcl = New-Object System.Security.AccessControl.DirectorySecurity $AdminACL = New-Object System.Security.AccessControl.FileSystemAccessRule('Builtin\Administrators','FullControl','ContainerInherit,ObjectInherit','InheritOnly','Allow') $FileAdminAcl.AddAccessRule($AdminACL) $DirAdminAcl.AddAccessRule($AdminACL) Try { $Item = Get-Item -LiteralPath $Item -Force -ErrorAction Stop If (-NOT $Item.PSIsContainer) { If ($PSCmdlet.ShouldProcess($Item, 'Set File Owner')) { Try { $Item.SetAccessControl($FileOwner) } Catch { Write-Warning "Couldn't take ownership of $($Item.FullName)! Taking FullControl of $($Item.Directory.FullName)" $Item.Directory.SetAccessControl($FileAdminAcl) $Item.SetAccessControl($FileOwner) } } } Else { If ($PSCmdlet.ShouldProcess($Item, 'Set Directory Owner')) { Try { $Item.SetAccessControl($DirOwner) } Catch { Write-Warning "Couldn't take ownership of $($Item.FullName)! Taking FullControl of $($Item.Parent.FullName)" $Item.Parent.SetAccessControl($DirAdminAcl) $Item.SetAccessControl($DirOwner) } } If ($Recurse) { [void]$PSBoundParameters.Remove('Path') Get-ChildItem $Item -Force | Set-Owner @PSBoundParameters } } } Catch { Write-Warning "$($Item): $($_.Exception.Message)" } } } End { #Remove priviledges that had been granted [void][TokenAdjuster]::RemovePrivilege("SeRestorePrivilege") [void][TokenAdjuster]::RemovePrivilege("SeBackupPrivilege") [void][TokenAdjuster]::RemovePrivilege("SeTakeOwnershipPrivilege") } } Function Test-RegistryEntry { <# .SYNOPSIS Tests the existence of a registry value .DESCRIPTION The Test-RegistryEntry cmdlet test the extistence of a registry value (property of a key). .PARAMETER Key The registry key to test for containing the property. .PARAMETER PropertyName The property name to test for. .EXAMPLE PS C:\>Test-RegistryEntry -Key "HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Component Based Servicing" -PropertyName PendingFileRenameOperations Returns true or false depending on the existence of the property .INPUTS System.String, System.String .OUTPUTS System.Boolean .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/28/2016 #> Param ( [Parameter(Position=0, ValueFromPipelineByPropertyName=$true, Mandatory=$true)] [string]$Key, [Parameter(Position=1, ValueFromPipelineByPropertyName=$true, Mandatory=$true)] [string]$PropertyName ) Get-ItemProperty -Path $Key -Name $PropertyName -ErrorAction SilentlyContinue | Out-Null return $? } Function Test-IsEnterpriseAdmin { <# .SYNOPSIS Tests if a user is a member of the Enterprise Admins group. .DESCRIPTION The Test-IsEnterpriseAdmin returns true if the user is in the group and false otherwise. .EXAMPLE Test-IsEnterpriseAdmin Determines if the user credentials being used to run the cmdlet have Enterprise Admin privileges. .EXAMPLE Test-IsEnterpriseAdmin -UserName "John Smith" Determines if the user John Smith has Enterprise Admin privileges. .EXAMPLE Test-IsEnterpriseAdmin -Credential (Get-Credential) Determines if the entered user credentials have Enterprise Admin privileges. .PARAMETER UserName The user to test the group membership on. If no user name is specified, the cmdlet runs against WindowsIdentity Principal. .PARAMETER Credential The PSCredential to use to test if the user has Enterprise Admin credentials. .INPUTS System.Management.Automation.PSCredential, System.String System.String, System.String .OUTPUTS System.Boolean .NOTES AUTHOR: Michael Haken LAST UPDATE: 2/24/2016 #> [CmdletBinding(DefaultParameterSetName="Username")] Param ( [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true,ParameterSetName="Username")] [string]$UserName = [System.String]::Empty, [Parameter(Position=0,ValueFromPipeLine=$true,ValueFromPipeLineByPropertyName=$true,ParameterSetName="Credential")] [PSCredential]$Credential = [PSCredential]::Empty ) Begin { Import-Module ActiveDirectory -ErrorAction Stop if ($Credential -eq $null) { $Credential = [System.Management.Automation.PSCredential]::Empty } [bool]$IsAdmin = $false } Process { switch ($PSCmdlet.ParameterSetName) { "Username" { if ($UserName -ne [System.String]::Empty) { $CurrentUser = $UserName } else { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent() if ($Principal.IsSystem) { Write-Host "Current principal is the SYSTEM account." $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { Write-Host "Current principal is a domain controller." $IsAdmin = $true } else { Write-Warning "Current principal is the SYSTEM account, but not a domain controller." $CurrentUser = $Principal.Name } } else { $CurrentUser = $Principal.Name } } } "Credential" { if ($Credential -ne [System.Management.Automation.PSCredential]::Empty) { $CurrentUser = $Credential.UserName } else { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent() if ($Principal.IsSystem) { Write-Host "Current principal is the SYSTEM account." $Role = Get-WmiObject -Class Win32_OperatingSystem -Property ProductType | Select-Object -ExpandProperty ProductType if ($Role -eq 2) { Write-Host "Current principal is a domain controller." $IsAdmin = $true } else { Write-Warning "Current principal is the SYSTEM account, but not a domain controller." $CurrentUser = $Principal.Name } } else { $CurrentUser = $Principal.Name } } } default { throw "Could not determine parameter set name for Test-IsEnterpriseAdmin." } } if(!$IsAdmin) { if ($CurrentUser.IndexOf("\") -ne -1) { $Domain = $CurrentUser.Substring(0, $CurrentUser.IndexOf("\")) $Forest = (Get-ADDomain -Identity $Domain).Forest $CurrentUser = $CurrentUser.Substring($CurrentUser.IndexOf("\") + 1) } elseif ($CurrentUser.IndexOf("@") -ne -1) { $Domain = $CurrentUser.Substring($CurrentUser.IndexOf("@") + 1) $Forest = (Get-ADDomain -Identity $Domain).Forest $CurrentUser = $CurrentUser.Substring(0, $CurrentUser.IndexOf("@")) } else { $Forest = (Get-ADForest -Current LoggedOnUser).Name } $Groups = Get-NestedGroupMembership -Principal $CurrentUser | Select-Object -Property Name,SID if($Credential -ne $null -and $Credential -ne [System.Management.Automation.PSCredential]::Empty) { $RootDomainSID = Get-ADDomain -Identity $Forest -Credential $Credential | Select-Object -ExpandProperty DomainSID } else { $RootDomainSID = Get-ADDomain -Identity $Forest | Select-Object -ExpandProperty DomainSID } [Security.Principal.SecurityIdentifier]$EnterpriseAdminSID = New-Object System.Security.Principal.SecurityIdentifier([System.Security.Principal.WellKnownSidType]::AccountEnterpriseAdminsSid, $RootDomainSID) foreach ($Group in $Groups) { if ($Group.SID -eq $EnterpriseAdminSID) { $IsAdmin = $true break } } } } End { Write-Output $IsAdmin } } Function Get-NestedGroupMembership { <# .SYNOPSIS Recursively gets the group membership of an AD principal. .DESCRIPTION The Get-NestedGroupMembership gets all nested group membership of an AD principal. .EXAMPLE Get-NestedGroupMembership -Principal Administrator Gets all group membership for the Administrator account. .PARAMETER Principal The principal to get group membership for. .INPUTS System.String .OUTPUTS Microsoft.ActiveDirectory.Management.ADGroup[] .NOTES AUTHOR: Michael Haken LAST UPDATE: 4/6/2016 #> Param( [Parameter(ValueFromPipelineByPropertyName=$true,ValueFromPipeline=$true,Position=0)] [string]$Principal, [Parameter(DontShow=$true)] [Microsoft.ActiveDirectory.Management.ADGroup[]]$Groups ) Begin { Import-Module ActiveDirectory -ErrorAction Stop if ($Groups -eq $null) { $Groups = @() } if ([System.String]::IsNullOrEmpty($Principal)) { $Principal = [System.Security.Principal.WindowsIdentity]::GetCurrent().Name } if ($Principal.IndexOf("DC=") -ne -1) { #Pulls the DC=...,DC=...,DC=... info and turns it into an FQDN $Server = $Principal.Substring($Principal.IndexOf("DC=")).Replace("DC=","").Replace(",",".") } else { if ($Principal.IndexOf("\") -ne -1) { $Domain = $Principal.Substring(0, $Principal.IndexOf("\")) $Server = (Get-ADDomain -Identity $Domain).DnsRoot $Principal = $Principal.Substring($Principal.IndexOf("\") + 1) } elseif ($Principal.IndexOf("@") -ne -1) { $Domain = $Principal.Substring($Principal.IndexOf("@") + 1) $Server = (Get-ADDomain -Identity $Domain).DnsRoot $Principal = $Principal.Substring(0, $Principal.IndexOf("@")) } else { $Server = (Get-ADDomain -Current LoggedOnUser).DnsRoot } } if ([System.String]::IsNullOrEmpty($Server)) { throw "Could not find a domain controller." } } Process { #Get the group membership of the evaluated principal $TempGroups = Get-ADPrincipalGroupMembership -Identity $Principal -Server $Server $GroupsToCheck = @() #Iterate through these groups, need to check if the Groups array already contains the group #If it doesn't, add the group since it is a newly discovered nested group, and also add it to the #array of groups to check for further nested group membership #We don't want to check the Groups array for nested membership since a lot of those groups have already been checked foreach($Group in $TempGroups) { if (!$Groups.Contains($Group)) { $Groups += $Group $GroupsToCheck += $Group } } #This array will hold newly discovered nested groups of the groups we need to check $NewGroups = @() #Get the nested group membership of each new group to check foreach ($Group in $GroupsToCheck) { $NewGroups += Get-NestedGroupMembership -Principal $Group.DistinguishedName -Groups $Groups } #After getting the nested groups, check to see if that group may have been added already to Groups #through nested membership in some other group that was already checked foreach ($Group in $NewGroups) { if (!$Groups.Contains($Group)) { $Groups += $Group } } } End { #Return the updated total group membership Write-Output $Groups } } |