diff --git a/Examples/Example-09-RemovingGPOPermissionUnknown02.ps1 b/Examples/Example-09-RemovingGPOPermissionUnknown02.ps1 new file mode 100644 index 0000000..fb7f237 --- /dev/null +++ b/Examples/Example-09-RemovingGPOPermissionUnknown02.ps1 @@ -0,0 +1,19 @@ +Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force + +# Step 1 - Create report +$Report = Get-GPOZaurrPermission -Type All +$Report | ConvertTo-Excel -FilePath $Env:UserProfile\Desktop\GPOOutput.xlsx -ExcelWorkSheetName 'GPO Permissions Before' -AutoFilter -AutoFit + +# Step 2 - Verify couple of GPOS returned with whatif +#Remove-GPOZaurrPermission -Verbose -Type Unknown -LimitProcessing 4 -WhatIf + +# Step 3 - Confirm the change without whatif +#Remove-GPOZaurrPermission -Verbose -Type Unknown -LimitProcessing 4 + +# Step 4 - Analyze GPO manually to confirm only unknown sids were removed + +# Step 5 - if everything went ok, continue process without whatif + +# Step-6 - Generate new report +#$Report = Get-GPOZaurrPermission -Type All +#$Report | ConvertTo-Excel -FilePath $Env:UserProfile\Desktop\GPOOutput.xlsx -ExcelWorkSheetName 'GPO Permissions After' -AutoFilter -AutoFit \ No newline at end of file diff --git a/Examples/Example-11-ReplaceGPOwner.ps1 b/Examples/Example-11-ReplaceGPOwner.ps1 new file mode 100644 index 0000000..b092f41 --- /dev/null +++ b/Examples/Example-11-ReplaceGPOwner.ps1 @@ -0,0 +1,12 @@ +Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force + +# Step 1 - Create report +$Report = Get-GPOZaurrOwner -IncludeSysvol +$Report | ConvertTo-Excel -FilePath $Env:UserProfile\Desktop\GPOOwners.xlsx -ExcelWorkSheetName 'GPO Owners Before' -AutoFilter -AutoFit + +# Step 2 - Fix owners / use WhatIf and LimitProcessing / verify changes before/after +Set-GPOZaurrOwner -Type NotAdministrative -Verbose -LimitProcessing 2 -WhatIf + +# Step 3 +$Report = Get-GPOZaurrOwner -IncludeSysvol +$Report | ConvertTo-Excel -FilePath $Env:UserProfile\Desktop\GPOOwners.xlsx -ExcelWorkSheetName 'GPO Owners After' -AutoFilter -AutoFit \ No newline at end of file diff --git a/Examples/Example-18-ListInconsistenciesACLAdvanced.ps1 b/Examples/Example-18-ListInconsistenciesACLAdvanced.ps1 index a82b837..e3dcbae 100644 --- a/Examples/Example-18-ListInconsistenciesACLAdvanced.ps1 +++ b/Examples/Example-18-ListInconsistenciesACLAdvanced.ps1 @@ -1,9 +1,9 @@ Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force #Get-GPOZaurrPermissionConsistency -Type All -Forest 'test.evotec.pl' | Format-Table -$Output = Get-GPOZaurrPermissionConsistency -GPOName 'Default Domain Controllers Policy' -IncludeDomains 'ad.evotec.xyz' -VerifyInside +$Output = Get-GPOZaurrPermissionConsistency -GPOName 'Default Domain Controllers Policy' -IncludeDomains 'ad.evotec.xyz' -VerifyInheritance $Output | Format-Table DisplayName, DomainName, ACLConsistent, ACLConsistentInside $Output.ACLConsistentInsideDetails | Format-Table -$Output = Get-GPOZaurrPermissionConsistency -VerifyInside -Type 'All' +$Output = Get-GPOZaurrPermissionConsistency -VerifyInheritance -Type 'All' $Output | Format-Table \ No newline at end of file diff --git a/Examples/Example-19-TestSysvol.ps1 b/Examples/Example-19-TestSysvol.ps1 new file mode 100644 index 0000000..4a1def2 --- /dev/null +++ b/Examples/Example-19-TestSysvol.ps1 @@ -0,0 +1,3 @@ +Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force + +Get-GPOZaurrSysvol -Verbose | Format-Table \ No newline at end of file diff --git a/Examples/Example-19-TestSysvolDomainControllers.ps1 b/Examples/Example-19-TestSysvolDomainControllers.ps1 new file mode 100644 index 0000000..00d3cab --- /dev/null +++ b/Examples/Example-19-TestSysvolDomainControllers.ps1 @@ -0,0 +1,3 @@ +Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force + +Get-GPOZaurrSysvol -VerifyDomainControllers -Verbose | Format-Table \ No newline at end of file diff --git a/GPOZaurr.psd1 b/GPOZaurr.psd1 index e96b7b5..28167b8 100644 --- a/GPOZaurr.psd1 +++ b/GPOZaurr.psd1 @@ -5,9 +5,9 @@ CompatiblePSEditions = 'Desktop' Copyright = '(c) 2011 - 2020 Przemyslaw Klys @ Evotec. All rights reserved.' Description = 'Group Policy Eater' - FunctionsToExport = 'Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrLink', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurrPermission', 'New-GPOZaurrWMI', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner' + FunctionsToExport = 'Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrLink', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrSysvol', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurrPermission', 'New-GPOZaurrWMI', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Set-GPOOwner', 'Set-GPOZaurrOwner' GUID = 'f7d4c9e4-0298-4f51-ad77-e8e3febebbde' - ModuleVersion = '0.0.27' + ModuleVersion = '0.0.29' PowerShellVersion = '5.1' PrivateData = @{ PSData = @{ @@ -21,7 +21,7 @@ ModuleName = 'PSSharedGoods' Guid = 'ee272aa8-baaa-4edf-9f45-b6d6f7d844fe' }, @{ - ModuleVersion = '0.0.56' + ModuleVersion = '0.0.57' ModuleName = 'ADEssentials' Guid = '9fc9fd61-7f11-4f4b-a527-084086f1905f' }, 'ActiveDirectory', 'GroupPolicy', 'CimCmdlets', 'Microsoft.PowerShell.Management', 'Microsoft.PowerShell.Utility' diff --git a/Private/Test-SysvolFolders.ps1 b/Private/Test-SysvolFolders.ps1 new file mode 100644 index 0000000..bb52268 --- /dev/null +++ b/Private/Test-SysvolFolders.ps1 @@ -0,0 +1,105 @@ +function Test-SysVolFolders { + [cmdletBinding()] + param( + [Array] $GPOs, + [string] $Server, + [string] $Domain + ) + $Differences = @{ } + $SysvolHash = @{ } + + $GPOGUIDS = $GPOs.ID.GUID + try { + $SYSVOL = Get-ChildItem -Path "\\$($Server)\SYSVOL\$Domain\Policies" -ErrorAction Stop + } catch { + $Sysvol = $Null + } + foreach ($_ in $SYSVOL) { + $GUID = $_.Name -replace '{' -replace '}' + $SysvolHash[$GUID] = $_ + } + $Files = $SYSVOL.Name -replace '{' -replace '}' + if ($Files) { + $Comparing = Compare-Object -ReferenceObject $GPOGUIDS -DifferenceObject $Files -IncludeEqual + foreach ($_ in $Comparing) { + if ($_.SideIndicator -eq '==') { + $Found = 'Exists' + } elseif ($_.SideIndicator -eq '<=') { + $Found = 'Not available on SYSVOL' + } elseif ($_.SideIndicator -eq '=>') { + $Found = 'Orphaned GPO' + } else { + $Found = 'Orphaned GPO' + } + $Differences[$_.InputObject] = $Found + } + } + $GPOSummary = @( + foreach ($GPO in $GPOS) { + if ($null -ne $SysvolHash[$GPO.Id.GUID].FullName) { + try { + $ACL = Get-Acl -Path $SysvolHash[$GPO.Id.GUID].FullName -ErrorAction Stop + } catch { + Write-Warning "Get-WinADGPOSysvolFolders - ACL reading failed for $($SysvolHash[$GPO.Id.GUID].FullName) with error: $($_.Exception.Message)" + $ACL = $null + } + } else { + $ACL = $null + } + if ($null -eq $Differences[$GPO.Id.Guid]) { + $SysVolStatus = 'Not available on SYSVOL' + } else { + $SysVolStatus = $Differences[$GPO.Id.Guid] + } + [PSCustomObject] @{ + DisplayName = $GPO.DisplayName + Status = $Differences[$GPO.Id.Guid] + DomainName = $GPO.DomainName + SysvolServer = $Server + SysvolStatus = $SysVolStatus + Owner = $GPO.Owner + FileOwner = $ACL.Owner + Id = $GPO.Id.Guid + GpoStatus = $GPO.GpoStatus + Description = $GPO.Description + CreationTime = $GPO.CreationTime + ModificationTime = $GPO.ModificationTime + UserVersion = $GPO.UserVersion + ComputerVersion = $GPO.ComputerVersion + WmiFilter = $GPO.WmiFilter + } + } + # Now we need to list thru Sysvol files and fine those that do not exists as GPO and create dummy GPO objects to show orphaned gpos + foreach ($_ in $Differences.Keys) { + if ($Differences[$_] -eq 'Orphaned GPO') { + if ($SysvolHash[$_].BaseName -notcontains 'PolicyDefinitions') { + + if ($null -ne $SysvolHash[$_].FullName) { + $ACL = Get-Acl -Path $SysvolHash[$_].FullName -ErrorAction SilentlyContinue + } else { + $ACL = $null + } + + [PSCustomObject] @{ + DisplayName = $SysvolHash[$_].BaseName + Status = 'Orphaned GPO' + DomainName = $Domain + SysvolServer = $Server + SysvolStatus = $Differences[$GPO.Id.Guid] + Owner = $ACL.Owner + FileOwner = $ACL.Owner + Id = $_ + GpoStatus = 'Orphaned' + Description = $null + CreationTime = $SysvolHash[$_].CreationTime + ModificationTime = $SysvolHash[$_].LastWriteTime + UserVersion = $null + ComputerVersion = $null + WmiFilter = $null + } + } + } + } + ) + $GPOSummary | Sort-Object -Property DisplayName +} \ No newline at end of file diff --git a/Public/Get-GPOZaurrOwner.ps1 b/Public/Get-GPOZaurrOwner.ps1 index 4aaa01d..3f877ed 100644 --- a/Public/Get-GPOZaurrOwner.ps1 +++ b/Public/Get-GPOZaurrOwner.ps1 @@ -35,13 +35,12 @@ Write-Verbose "Get-GPOZaurrOwner - Processing GPO: $($_.DisplayName) from domain: $($_.DomainName)" $ACL = Get-ADACLOwner -ADObject $_.GPODistinguishedName -Resolve -ADAdministrativeGroups $ADAdministrativeGroups $Object = [ordered] @{ - DisplayName = $_.DisplayName - DomainName = $_.DomainName - GUID = $_.GUID - DistinguishedName = $_.GPODistinguishedName - Owner = $ACL.OwnerName - OwnerSid = $ACL.OwnerSid - OwnerType = $ACL.OwnerType + DisplayName = $_.DisplayName + DomainName = $_.DomainName + GUID = $_.GUID + Owner = $ACL.OwnerName + OwnerSid = $ACL.OwnerSid + OwnerType = $ACL.OwnerType } if ($IncludeSysvol) { $FileOwner = Get-FileOwner -JustPath -Path $_.Path -Resolve @@ -49,7 +48,9 @@ $Object['SysvolSid'] = $FileOwner.OwnerSid $Object['SysvolType'] = $FileOwner.OwnerType $Object['SysvolPath'] = $_.Path + $Object['IsOwnerConsistent'] = if ($ACL.OwnerName -eq $FileOwner.OwnerName) { $true } else { $false } } + $Object['DistinguishedName'] = $_.GPODistinguishedName [PSCUstomObject] $Object } } diff --git a/Public/Get-GPOZaurrPermissionConsistency.ps1 b/Public/Get-GPOZaurrPermissionConsistency.ps1 index 973d8a8..b124c68 100644 --- a/Public/Get-GPOZaurrPermissionConsistency.ps1 +++ b/Public/Get-GPOZaurrPermissionConsistency.ps1 @@ -9,7 +9,7 @@ [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation, [switch] $IncludeGPOObject, - [switch] $VerifyInside + [switch] $VerifyInheritance ) Begin { $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation @@ -49,7 +49,7 @@ $IsConsistent = 'Not available.' } $SysVolpath = -join ('\\', $Domain, '\sysvol\', $Domain, '\Policies\{', $_.ID.GUID, '}') - if ($VerifyInside) { + if ($VerifyInheritance) { $FolderPermissions = Get-WinADSharePermission -Path $SysVolpath [Array] $NotInheritedPermissions = foreach ($File in $FolderPermissions) { if ($File.Path -ne $SysVolpath -and $File.IsInherited -eq $false) { @@ -65,30 +65,54 @@ $ACLConsistentInside = $null } $Object = [ordered] @{ - DisplayName = $_.DisplayName # : New Group Policy Object - DomainName = $_.DomainName # : ad.evotec.xyz - ACLConsistent = $IsConsistent - ACLConsistentInside = $ACLConsistentInside - Owner = $_.Owner # : EVOTEC\Enterprise Admins - Path = $_.Path - SysVolPath = $SysvolPath - Id = $_.Id # : 8a7bc515-d7fd-4d1f-90b8-e47c15f89295 - GpoStatus = $_.GpoStatus # : AllSettingsEnabled - Description = $_.Description # : - CreationTime = $_.CreationTime # : 04.03.2020 17:19:42 - ModificationTime = $_.ModificationTime# : 06.05.2020 10:30:36 - UserVersion = $_.UserVersion # : AD Version: 0, SysVol Version: 0 - ComputerVersion = $_.ComputerVersion # : AD Version: 1, SysVol Version: 1 - WmiFilter = $_.WmiFilter # : - Error = $ErrorMessage + DisplayName = $_.DisplayName # : New Group Policy Object + DomainName = $_.DomainName # : ad.evotec.xyz + ACLConsistent = $IsConsistent } + if ($VerifyInheritance) { + $Object['ACLConsistentInside'] = $ACLConsistentInside + } + $Object['Owner'] = $_.Owner # : EVOTEC\Enterprise Admins + $Object['Path'] = $_.Path + $Object['SysVolPath '] = $SysvolPath + $Object['Id '] = $_.Id # : 8a7bc515-d7fd-4d1f-90b8-e47c15f89295 + $Object['GpoStatus'] = $_.GpoStatus # : AllSettingsEnabled + $Object['Description'] = $_.Description # : + $Object['CreationTime'] = $_.CreationTime # : 04.03.2020 17:19:42 + $Object['ModificationTime'] = $_.ModificationTime# : 06.05.2020 10:30:36 + $Object['UserVersion'] = $_.UserVersion # : AD Version: 0, SysVol Version: 0 + $Object['ComputerVersion'] = $_.ComputerVersion # : AD Version: 1, SysVol Version: 1 + $Object['WmiFilter'] = $_.WmiFilter # : + $Object['Error'] = $ErrorMessage if ($IncludeGPOObject) { $Object['IncludeGPOObject'] = $_ } - if ($VerifyInside) { + if ($VerifyInheritance) { $Object['ACLConsistentInsideDetails'] = $NotInheritedPermissions } - [PSCustomObject] $Object + if ($Type -eq 'All') { + [PSCustomObject] $Object + } elseif ($Type -eq 'Inconsistent') { + if ($VerifyInheritance) { + if (-not $IsConsistent -or -not $ACLConsistentInside) { + [PSCustomObject] $Object + } + } else { + if (-not $IsConsistent) { + [PSCustomObject] $Object + } + } + } elseif ($Type -eq 'Consistent') { + if ($VerifyInheritance) { + if ($IsConsistent -and $ACLConsistentInside) { + [PSCustomObject] $Object + } + } else { + if ($IsConsistent) { + [PSCustomObject] $Object + } + } + } } } } diff --git a/Public/Get-GPOZaurrSysvol.ps1 b/Public/Get-GPOZaurrSysvol.ps1 new file mode 100644 index 0000000..9f0f409 --- /dev/null +++ b/Public/Get-GPOZaurrSysvol.ps1 @@ -0,0 +1,34 @@ +function Get-GPOZaurrSysvol { + [cmdletBinding()] + param( + [alias('ForestName')][string] $Forest, + [string[]] $ExcludeDomains, + [string[]] $ExcludeDomainControllers, + [alias('Domain', 'Domains')][string[]] $IncludeDomains, + [alias('DomainControllers')][string[]] $IncludeDomainControllers, + [switch] $SkipRODC, + [Array] $GPOs, + [System.Collections.IDictionary] $ExtendedForestInformation, + [switch] $VerifyDomainControllers + ) + $ForestInformation = Get-WinADForestDetails -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExcludeDomainControllers $ExcludeDomainControllers -IncludeDomainControllers $IncludeDomainControllers -SkipRODC:$SkipRODC -ExtendedForestInformation $ExtendedForestInformation + if (-not $VerifyDomainControllers) { + foreach ($Domain in $ForestInformation.Domains) { + Write-Verbose "Get-WinADGPOSysvolFolders - Processing $Domain" + $QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0] + [Array]$GPOs = @(Get-GPO -All -Domain $Domain -Server $QueryServer) + Test-SysVolFolders -GPOs $GPOs -Server $Domain -Domain $Domain + } + } else { + foreach ($Domain in $ForestInformation.Domains) { + Write-Verbose "Get-WinADGPOSysvolFolders - Processing $Domain" + $QueryServer = $ForestInformation['QueryServers']["$Domain"].HostName[0] + [Array]$GPOs = @(Get-GPO -All -Domain $Domain -Server $QueryServer) + foreach ($Server in $ForestInformation['DomainDomainControllers']["$Domain"]) { + Write-Verbose "Get-WinADGPOSysvolFolders - Processing $Domain \ $($Server.HostName.Trim())" + Test-SysVolFolders -GPOs $GPOs -Server $Server.Hostname -Domain $Domain + } + } + } +} +