This function requires Administrative privileges (Domain Admin or similar) because it needs to see permissions. If you removed Authenticated Users from a GPO, you wouldn't be able to see this GPO as a standard user. You also need to have ActiveDirectory and GPO module installed from RSAT. That's a standard for Domain Admins.
function Get-WinADGPOMissingPermissions {
<#
.SYNOPSIS
Short description
.DESCRIPTION
Long description
.PARAMETER Domain
Parameter description
.EXAMPLE
An example
.NOTES
Based on https://secureinfra.blog/2018/12/31/most-common-mistakes-in-active-directory-and-domain-services-part-1/
#>
[cmdletBinding()]
param([alias('ForestName')][string] $Forest,
[string[]] $ExcludeDomains,
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
[switch] $SkipRODC,
[System.Collections.IDictionary] $ExtendedForestInformation,
[validateset('AuthenticatedUsers', 'DomainComputers', 'Either')][string] $Mode = 'Either',
[switch] $Extended)
if (-not $ExtendedForestInformation) { $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC } else { $ForestInformation = $ExtendedForestInformation }
foreach ($Domain in $ForestInformation.Domains) {
$QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0]
if ($Extended) {
$GPOs = Get-GPO -All -Domain $Domain -Server $QueryServer | ForEach-Object { [xml] $XMLContent = Get-GPOReport -ID $_.ID.Guid -ReportType XML -Server $ForestInformation.QueryServers[$Domain].HostName[0] -Domain $Domain
Add-Member -InputObject $_ -MemberType NoteProperty -Name 'LinksTo' -Value $XMLContent.GPO.LinksTo
if ($XMLContent.GPO.LinksTo) { Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Linked' -Value $true } else { Add-Member -InputObject $_ -MemberType NoteProperty -Name 'Linked' -Value $false }
$_ | Select-Object -Property Id, DisplayName, DomainName, Owner, Linked, GpoStatus, CreationTime, ModificationTime,
@{label = 'UserVersion'
expression = { "AD Version: $($_.User.DSVersion), SysVol Version: $($_.User.SysvolVersion)" }
},
@{label = 'ComputerVersion'
expression = { "AD Version: $($_.Computer.DSVersion), SysVol Version: $($_.Computer.SysvolVersion)" }
}, WmiFilter, Description, User, Computer, LinksTo }
} else { $GPOs = Get-GPO -All -Domain $Domain -Server $QueryServer }
$DomainInformation = Get-ADDomain -Server $QueryServer
$DomainComputersSID = $('{0}-515' -f $DomainInformation.DomainSID.Value)
$MissingPermissions = @(foreach ($GPO in $GPOs) {
$Permissions = Get-GPPermission -Guid $GPO.Id -All -Server $QueryServer -DomainName $Domain | Select-Object -ExpandProperty Trustee
if ($Mode -eq 'Either' -or $Mode -eq 'AuthenticatedUsers') { $GPOPermissionForAuthUsers = $Permissions | Where-Object { $_.Sid.Value -eq 'S-1-5-11' } }
if ($Mode -eq 'Either' -or $Mode -eq 'DomainComputers') { $GPOPermissionForDomainComputers = $Permissions | Where-Object { $_.Sid.Value -eq $DomainComputersSID } }
if ($Mode -eq 'Either') { If (-not $GPOPermissionForAuthUsers -and -not $GPOPermissionForDomainComputers) { $GPO } } elseif ($Mode -eq 'AuthenticatedUsers') { If (-not $GPOPermissionForAuthUsers) { $GPO } } elseif ($Mode -eq 'DomainComputers') { If (-not $GPOPermissionForDomainComputers) { $GPO } }
})
$MissingPermissions
}
}
function Get-WinADForestDetails {
[CmdletBinding()]
param([alias('ForestName')][string] $Forest,
[string[]] $ExcludeDomains,
[string[]] $ExcludeDomainControllers,
[alias('Domain', 'Domains')][string[]] $IncludeDomains,
[alias('DomainControllers', 'ComputerName')][string[]] $IncludeDomainControllers,
[switch] $SkipRODC,
[string] $Filter = '*',
[switch] $TestAvailability,
[ValidateSet('All', 'Ping', 'WinRM', 'PortOpen', 'Ping+WinRM', 'Ping+PortOpen', 'WinRM+PortOpen')] $Test = 'All',
[int[]] $Ports = 135,
[int] $PortsTimeout = 100,
[int] $PingCount = 1)
if ($Global:ProgressPreference -ne 'SilentlyContinue') {
$TemporaryProgress = $Global:ProgressPreference
$Global:ProgressPreference = 'SilentlyContinue'
}
$Findings = [ordered] @{ }
try { if ($Forest) { $ForestInformation = Get-ADForest -ErrorAction Stop -Identity $Forest } else { $ForestInformation = Get-ADForest -ErrorAction Stop } } catch {
Write-Warning "Get-WinADForestDetails - Error discovering DC for Forest - $($_.Exception.Message)"
return
}
if (-not $ForestInformation) { return }
$Findings['Forest'] = $ForestInformation
$Findings['ForestDomainControllers'] = @()
$Findings['QueryServers'] = @{ }
$Findings['QueryServers']['Forest'] = $DC
$Findings.Domains = foreach ($_ in $ForestInformation.Domains) {
if ($IncludeDomains) {
if ($_ -in $IncludeDomains) { $_.ToLower() }
continue
}
if ($_ -notin $ExcludeDomains) { $_.ToLower() }
}
$Findings['ForestDomainControllers'] = foreach ($Domain in $Findings.Domains) {
try { $DC = Get-ADDomainController -DomainName $Domain -Discover -ErrorAction Stop } catch {
Write-Warning "Get-WinADForestDetails - Error discovering DC for domain $Domain - $($_.Exception.Message)"
continue
}
$Findings['QueryServers']["$Domain"] = $DC
[Array] $AllDC = try {
try { $DomainControllers = Get-ADDomainController -Filter $Filter -Server $DC.HostName[0] -ErrorAction Stop } catch {
Write-Warning "Get-WinADForestDetails - Error listing DCs for domain $Domain - $($_.Exception.Message)"
continue
}
foreach ($S in $DomainControllers) {
if ($IncludeDomainControllers.Count -gt 0) { If (-not $IncludeDomainControllers[0].Contains('.')) { if ($S.Name -notin $IncludeDomainControllers) { continue } } else { if ($S.HostName -notin $IncludeDomainControllers) { continue } } }
if ($ExcludeDomainControllers.Count -gt 0) { If (-not $ExcludeDomainControllers[0].Contains('.')) { if ($S.Name -notin $ExcludeDomainControllers) { continue } } else { if ($S.HostName -in $ExcludeDomainControllers) { continue } } }
$Server = [ordered] @{Domain = $Domain
HostName = $S.HostName
Name = $S.Name
Forest = $ForestInformation.RootDomain
Site = $S.Site
IPV4Address = $S.IPV4Address
IPV6Address = $S.IPV6Address
IsGlobalCatalog = $S.IsGlobalCatalog
IsReadOnly = $S.IsReadOnly
IsSchemaMaster = ($S.OperationMasterRoles -contains 'SchemaMaster')
IsDomainNamingMaster = ($S.OperationMasterRoles -contains 'DomainNamingMaster')
IsPDC = ($S.OperationMasterRoles -contains 'PDCEmulator')
IsRIDMaster = ($S.OperationMasterRoles -contains 'RIDMaster')
IsInfrastructureMaster = ($S.OperationMasterRoles -contains 'InfrastructureMaster')
OperatingSystem = $S.OperatingSystem
OperatingSystemVersion = $S.OperatingSystemVersion
OperatingSystemLong = ConvertTo-OperatingSystem -OperatingSystem $S.OperatingSystem -OperatingSystemVersion $S.OperatingSystemVersion
LdapPort = $S.LdapPort
SslPort = $S.SslPort
DistinguishedName = $S.ComputerObjectDN
Pingable = $null
WinRM = $null
PortOpen = $null
Comment = ''
}
if ($TestAvailability) {
if ($Test -eq 'All' -or $Test -like 'Ping*') { $Server.Pingable = Test-Connection -ComputerName $Server.IPV4Address -Quiet -Count $PingCount }
if ($Test -eq 'All' -or $Test -like '*WinRM*') { $Server.WinRM = (Test-WinRM -ComputerName $Server.HostName).Status }
if ($Test -eq 'All' -or '*PortOpen*') { $Server.PortOpen = (Test-ComputerPort -Server $Server.HostName -PortTCP $Ports -Timeout $PortsTimeout).Status }
}
[PSCustomObject] $Server
}
} catch {
[PSCustomObject]@{Domain = $Domain
HostName = ''
Name = ''
Forest = $ForestInformation.RootDomain
IPV4Address = ''
IPV6Address = ''
IsGlobalCatalog = ''
IsReadOnly = ''
Site = ''
SchemaMaster = $false
DomainNamingMasterMaster = $false
PDCEmulator = $false
RIDMaster = $false
InfrastructureMaster = $false
LdapPort = ''
SslPort = ''
DistinguishedName = ''
Pingable = $null
WinRM = $null
PortOpen = $null
Comment = $_.Exception.Message -replace "`n", " " -replace "`r", " "
}
}
if ($SkipRODC) { $Findings[$Domain] = $AllDC | Where-Object { $_.IsReadOnly -eq $false } } else { $Findings[$Domain] = $AllDC }
$Findings[$Domain]
}
if ($TemporaryProgress) { $Global:ProgressPreference = $TemporaryProgress }
$Findings
}
function ConvertTo-OperatingSystem {
[CmdletBinding()]
param([string] $OperatingSystem,
[string] $OperatingSystemVersion)
if ($OperatingSystem -like '*Windows 10*') {
$Systems = @{'10.0 (18363)' = "Windows 10 1909"
'10.0 (18362)' = "Windows 10 1903"
'10.0 (17763)' = "Windows 10 1809"
'10.0 (17134)' = "Windows 10 1803"
'10.0 (16299)' = "Windows 10 1709"
'10.0 (15063)' = "Windows 10 1703"
'10.0 (14393)' = "Windows 10 1607"
'10.0 (10586)' = "Windows 10 1511"
'10.0 (10240)' = "Windows 10 1507"
'10.0 (18898)' = 'Windows 10 Insider Preview'
'10.0.18363' = "Windows 10 1909"
'10.0.18362' = "Windows 10 1903"
'10.0.17763' = "Windows 10 1809"
'10.0.17134' = "Windows 10 1803"
'10.0.16299' = "Windows 10 1709"
'10.0.15063' = "Windows 10 1703"
'10.0.14393' = "Windows 10 1607"
'10.0.10586' = "Windows 10 1511"
'10.0.10240' = "Windows 10 1507"
'10.0.18898' = 'Windows 10 Insider Preview'
}
$System = $Systems[$OperatingSystemVersion]
if (-not $System) { $System = $OperatingSystem }
} elseif ($OperatingSystem -like '*Windows Server*') {
$Systems = @{'5.2 (3790)' = 'Windows Server 2003'
'6.1 (7601)' = 'Windows Server 2008 R2'
'10.0 (18362)' = "Windows Server, version 1903 (Semi-Annual Channel) 1903"
'10.0 (17763)' = "Windows Server 2019 (Long-Term Servicing Channel) 1809"
'10.0 (17134)' = "Windows Server, version 1803 (Semi-Annual Channel) 1803"
'10.0 (14393)' = "Windows Server 2016 (Long-Term Servicing Channel) 1607"
'10.0.18362' = "Windows Server, version 1903 (Semi-Annual Channel) 1903"
'10.0.17763' = "Windows Server 2019 (Long-Term Servicing Channel) 1809"
'10.0.17134' = "Windows Server, version 1803 (Semi-Annual Channel) 1803"
'10.0.14393' = "Windows Server 2016 (Long-Term Servicing Channel) 1607"
}
$System = $Systems[$OperatingSystemVersion]
if (-not $System) { $System = $OperatingSystem }
} else { $System = $OperatingSystem }
if ($System) { $System } else { 'Unknown' }
}
function Test-ComputerPort {
[CmdletBinding()]
param ([alias('Server')][string[]] $ComputerName,
[int[]] $PortTCP,
[int[]] $PortUDP,
[int]$Timeout = 5000)
begin {
if ($Global:ProgressPreference -ne 'SilentlyContinue') {
$TemporaryProgress = $Global:ProgressPreference
$Global:ProgressPreference = 'SilentlyContinue'
}
}
process {
foreach ($Computer in $ComputerName) {
foreach ($P in $PortTCP) {
$Output = [ordered] @{'ComputerName' = $Computer
'Port' = $P
'Protocol' = 'TCP'
'Status' = $null
'Summary' = $null
'Response' = $null
}
$TcpClient = Test-NetConnection -ComputerName $Computer -Port $P -InformationLevel Detailed -WarningAction SilentlyContinue
if ($TcpClient.TcpTestSucceeded) {
$Output['Status'] = $TcpClient.TcpTestSucceeded
$Output['Summary'] = "TCP $P Successful"
} else {
$Output['Status'] = $false
$Output['Summary'] = "TCP $P Failed"
$Output['Response'] = $Warnings
}
[PSCustomObject]$Output
}
foreach ($P in $PortUDP) {
$Output = [ordered] @{'ComputerName' = $Computer
'Port' = $P
'Protocol' = 'UDP'
'Status' = $null
'Summary' = $null
}
$UdpClient = [System.Net.Sockets.UdpClient]::new($Computer, $P)
$UdpClient.Client.ReceiveTimeout = $Timeout
$Encoding = [System.Text.ASCIIEncoding]::new()
$byte = $Encoding.GetBytes("Evotec")
[void]$UdpClient.Send($byte, $byte.length)
$RemoteEndpoint = [System.Net.IPEndPoint]::new([System.Net.IPAddress]::Any, 0)
try {
$Bytes = $UdpClient.Receive([ref]$RemoteEndpoint)
[string]$Data = $Encoding.GetString($Bytes)
If ($Data) {
$Output['Status'] = $true
$Output['Summary'] = "UDP $P Successful"
$Output['Response'] = $Data
}
} catch {
$Output['Status'] = $false
$Output['Summary'] = "UDP $P Failed"
$Output['Response'] = $_.Exception.Message
}
$UdpClient.Close()
$UdpClient.Dispose()
[PSCustomObject]$Output
}
}
}
end { if ($TemporaryProgress) { $Global:ProgressPreference = $TemporaryProgress } }
}
function Test-WinRM {
[CmdletBinding()]
param ([alias('Server')][string[]] $ComputerName)
$Output = foreach ($Computer in $ComputerName) {
$Test = [PSCustomObject] @{Output = $null
Status = $null
ComputerName = $Computer
}
try {
$Test.Output = Test-WSMan -ComputerName $Computer -ErrorAction Stop
$Test.Status = $true
} catch { $Test.Status = $false }
$Test
}
$Output
}
$MissingPermissions = Get-WinADGPOMissingPermissions -Mode Either
$MissingPermissions | Format-Table -AutoSize