diff --git a/Examples/Example-20-RemoveOrphanedFoldersSysvol.ps1 b/Examples/Example-20-RemoveOrphanedFoldersSysvol.ps1 index d1f46c6..919ae99 100644 --- a/Examples/Example-20-RemoveOrphanedFoldersSysvol.ps1 +++ b/Examples/Example-20-RemoveOrphanedFoldersSysvol.ps1 @@ -4,4 +4,4 @@ Remove-GPOZaurrOrphanedSysvolFolders -Verbose -WhatIf -LimitProcessing 2 -IncludeDomains 'ad.evotec.xyz' # this runs for whole SYSVOL and checks things against GPOS -Remove-GPOZaurrOrphanedSysvolFolders -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup -WhatIf \ No newline at end of file +Remove-GPOZaurrOrphanedSysvolFolders -Verbose -IncludeDomains 'ad.evotec.xyz' -BackupPath $Env:UserProfile\Desktop\MyBackup1 #-WhatIf \ No newline at end of file diff --git a/Examples/Example-22-RemoveLegacyADMFiles.ps1 b/Examples/Example-22-RemoveLegacyADMFiles.ps1 index 2db82bc..15c7518 100644 --- a/Examples/Example-22-RemoveLegacyADMFiles.ps1 +++ b/Examples/Example-22-RemoveLegacyADMFiles.ps1 @@ -1,3 +1,3 @@ Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force -Remove-GPOZaurrLegacyFiles -Verbose -WhatIf \ No newline at end of file +Remove-GPOZaurrLegacyFiles -Verbose -BackupPath $Env:USERPROFILE\Desktop\BackupADM -BackupDated -RemoveEmptyFolders #-WhatIf #-LimitProcessing 2 -WhatIf \ No newline at end of file diff --git a/Examples/Example-31-FindAndRemoveEmptyFolders.ps1 b/Examples/Example-31-FindAndRemoveEmptyFolders.ps1 new file mode 100644 index 0000000..b963189 --- /dev/null +++ b/Examples/Example-31-FindAndRemoveEmptyFolders.ps1 @@ -0,0 +1,5 @@ +Import-Module "$PSScriptRoot\..\GPoZaurr.psd1" -Force + +Get-GPOZaurrEmptyFolders | Where-Object { $_.Name -eq 'Adm' } | Format-Table * + +Remove-GPOZaurrEmptyFolders -Folders Adm -Verbose -LimitProcessing 1 \ No newline at end of file diff --git a/GPOZaurr.psd1 b/GPOZaurr.psd1 index 04f53a4..a206e1c 100644 --- a/GPOZaurr.psd1 +++ b/GPOZaurr.psd1 @@ -5,7 +5,7 @@ CompatiblePSEditions = 'Desktop' Copyright = '(c) 2011 - 2020 Przemyslaw Klys @ Evotec. All rights reserved.' Description = 'Group Policy Eater is a PowerShell module that aims to gather information about Group Policies but also allows fixing issues that you may find in them.' - FunctionsToExport = 'Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinitions', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrSysvol', 'Get-WMIFilter', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurr', 'Invoke-GPOZaurrPermission', 'New-GPOZaurrWMI', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrLegacyFiles', 'Remove-GPOZaurrOrphanedSysvolFolders', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Select-GPOTranslation', 'Set-GPOOwner', 'Set-GPOZaurrOwner' + FunctionsToExport = 'Add-GPOPermission', 'Add-GPOZaurrPermission', 'Backup-GPOZaurr', 'Get-GPOZaurr', 'Get-GPOZaurrAD', 'Get-GPOZaurrBackupInformation', 'Get-GPOZaurrEmptyFolders', 'Get-GPOZaurrFiles', 'Get-GPOZaurrFilesPolicyDefinitions', 'Get-GPOZaurrLegacyFiles', 'Get-GPOZaurrLink', 'Get-GPOZaurrLinkSummary', 'Get-GPOZaurrOwner', 'Get-GPOZaurrPassword', 'Get-GPOZaurrPermission', 'Get-GPOZaurrPermissionConsistency', 'Get-GPOZaurrSysvol', 'Get-WMIFilter', 'Get-GPOZaurrWMI', 'Invoke-GPOZaurr', 'Invoke-GPOZaurrPermission', 'New-GPOZaurrWMI', 'Remove-GPOPermission', 'Remove-GPOZaurr', 'Remove-GPOZaurrEmptyFolders', 'Remove-GPOZaurrLegacyFiles', 'Remove-GPOZaurrOrphanedSysvolFolders', 'Remove-GPOZaurrPermission', 'Remove-GPOZaurrWMI', 'Repair-GPOZaurrPermissionConsistency', 'Restore-GPOZaurr', 'Save-GPOZaurrFiles', 'Select-GPOTranslation', 'Set-GPOOwner', 'Set-GPOZaurrOwner' GUID = 'f7d4c9e4-0298-4f51-ad77-e8e3febebbde' ModuleVersion = '0.0.48' PowerShellVersion = '5.1' diff --git a/Public/Get-GPOZaurrEmptyFolders.ps1 b/Public/Get-GPOZaurrEmptyFolders.ps1 new file mode 100644 index 0000000..1a9ea3e --- /dev/null +++ b/Public/Get-GPOZaurrEmptyFolders.ps1 @@ -0,0 +1,29 @@ +function Get-GPOZaurrEmptyFolders { + [cmdletBinding()] + param( + [alias('ForestName')][string] $Forest, + [string[]] $ExcludeDomains, + [alias('Domain', 'Domains')][string[]] $IncludeDomains, + [System.Collections.IDictionary] $ExtendedForestInformation, + [switch] $AllFolders + ) + $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation + foreach ($Domain in $ForestInformation.Domains) { + Get-ChildItem -Path "\\$Domain\SYSVOL\$Domain\policies" -ErrorAction SilentlyContinue -Recurse -ErrorVariable err -Force -Directory | ForEach-Object { + $FullFolder = Test-Path -Path "$($_.FullName)\*" + if (-not $FullFolder -or $AllFolders) { + [PSCustomObject] @{ + FullName = $_.FullName + IsEmptyFolder = -not $FullFolder + Name = $_.Name + Root = $_.Root + Parent = $_.Parent + CreationTime = $_.CreationTime + LastWriteTime = $_.LastWriteTime + Attributes = $_.Attributes + DomainName = $Domain + } + } + } + } +} \ No newline at end of file diff --git a/Public/Get-GPOZaurrLegacyFiles.ps1 b/Public/Get-GPOZaurrLegacyFiles.ps1 index 7abca8d..7ef3415 100644 --- a/Public/Get-GPOZaurrLegacyFiles.ps1 +++ b/Public/Get-GPOZaurrLegacyFiles.ps1 @@ -8,7 +8,17 @@ ) $ForestInformation = Get-WinADForestDetails -Extended -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation foreach ($Domain in $ForestInformation.Domains) { - Get-ChildItem -Path "\\$Domain\SYSVOL\$Domain\policies" -ErrorAction SilentlyContinue -Recurse -Include '*.adm' -ErrorVariable err | Select-Object Name, FullName, CreationTime, LastWriteTime, Attributes + Get-ChildItem -Path "\\$Domain\SYSVOL\$Domain\policies" -ErrorAction SilentlyContinue -Recurse -Include '*.adm', 'admfiles.ini' -ErrorVariable err -Force | ForEach-Object { + [PSCustomObject] @{ + Name = $_.Name + FullName = $_.FullName + CreationTime = $_.CreationTime + LastWriteTime = $_.LastWriteTime + Attributes = $_.Attributes + DomainName = $Domain + DirectoryName = $_.DirectoryName + } + } foreach ($e in $err) { Write-Warning "Get-GPOZaurrLegacy - $($e.Exception.Message) ($($e.CategoryInfo.Reason))" } diff --git a/Public/Remove-GPOZaurrEmptyFolders.ps1 b/Public/Remove-GPOZaurrEmptyFolders.ps1 new file mode 100644 index 0000000..be8b081 --- /dev/null +++ b/Public/Remove-GPOZaurrEmptyFolders.ps1 @@ -0,0 +1,25 @@ +function Remove-GPOZaurrEmptyFolders { + [cmdletBinding(SupportsShouldProcess)] + param( + [ValidateSet('Adm')][string] $Folders, + [int] $LimitProcessing = [int32]::MaxValue, + [alias('ForestName')][string] $Forest, + [string[]] $ExcludeDomains, + [alias('Domain', 'Domains')][string[]] $IncludeDomains, + [System.Collections.IDictionary] $ExtendedForestInformation + ) + Get-GPOZaurrEmptyFolders -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation | Where-Object { + foreach ($Folder in $Folders) { + if ($_.IsEmptyFolder -eq $true -and $_.Name -eq $Folder) { + $_ + } + } + } | Select-Object | Select-Object -First $LimitProcessing | ForEach-Object { + try { + Write-Verbose "Remove-GPOZaurrEmptyFolders - Removing $($_.FullName)" + Remove-Item -Path $_.FullName + } catch { + Write-Warning "Remove-GPOZaurrEmptyFolders - Failed to remove directory $($_.FullName): $($_.Exception.Message)." + } + } +} \ No newline at end of file diff --git a/Public/Remove-GPOZaurrLegacyFiles.ps1 b/Public/Remove-GPOZaurrLegacyFiles.ps1 index 46eb50a..30dc481 100644 --- a/Public/Remove-GPOZaurrLegacyFiles.ps1 +++ b/Public/Remove-GPOZaurrLegacyFiles.ps1 @@ -1,12 +1,24 @@ function Remove-GPOZaurrLegacyFiles { [cmdletBinding(SupportsShouldProcess)] param( + [string] $BackupPath, + [switch] $BackupDated, + [switch] $RemoveEmptyFolders, [alias('ForestName')][string] $Forest, [string[]] $ExcludeDomains, [alias('Domain', 'Domains')][string[]] $IncludeDomains, [int] $LimitProcessing = [int32]::MaxValue ) + if ($BackupPath) { + if ($BackupDated) { + $BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))" + } else { + $BackupFinalPath = $BackupPath + } + } else { + $BackupFinalPath = '' + } $Splat = @{ Forest = $Forest IncludeDomains = $IncludeDomains @@ -14,12 +26,63 @@ ExtendedForestInformation = $ExtendedForestInformation Verbose = $VerbosePreference } - Get-GPOZaurrLegacyFiles @Splat | Select-Object -First $LimitProcessing | ForEach-Object { - try { - Remove-Item -Path $_.FullName -ErrorAction Stop - } catch { - $ErrorMessage = $_.Exception.Message - Write-Warning "Remove-GPOZaurrLegacyFiles - Failed to remove file $($_.FullName): $($ErrorMessage)." + [Array] $Deleted = Get-GPOZaurrLegacyFiles @Splat | Select-Object -First $LimitProcessing | ForEach-Object { + Write-Verbose "Remove-GPOZaurrLegacyFiles - Processing $($_.FullName)" + if ($BackupFinalPath) { + $SYSVOLRoot = "\\$($_.DomainName)\SYSVOL\$($_.DomainName)\policies\" + $DestinationFile = ($_.FullName).Replace($SYSVOLRoot, '') + #$DestinationMissingFolder = $DestinationFile.Replace($DestinationFile, '') + $DestinationFilePath = [system.io.path]::Combine($BackupFinalPath, $DestinationFile) + #$DestinationFolderPath = [system.io.path]::Combine($BackupFinalPath, $DestinationMissingFolder) + + Write-Verbose "Remove-GPOZaurrLegacyFiles - Backing up $($_.FullName)" + $Created = New-Item -ItemType File -Path $DestinationFilePath -Force + if ($Created) { + Try { + Copy-Item -LiteralPath $_.FullName -Recurse -Destination $DestinationFilePath -ErrorAction Stop -Force + $BackupWorked = $true + } catch { + Write-Warning "Remove-GPOZaurrLegacyFiles - Error backing up error: $($_.Exception.Message)" + $BackupWorked = $false + } + } else { + $BackupWorked = $false + } + } + if ($BackupWorked -or $BackupFinalPath -eq '') { + try { + Write-Verbose "Remove-GPOZaurrLegacyFiles - Deleting $($_.FullName)" + Remove-Item -Path $_.FullName -ErrorAction Stop -Force + $_ + } catch { + Write-Warning "Remove-GPOZaurrLegacyFiles - Failed to remove file $($_.FullName): $($_.Exception.Message)." + } + } + } + if ($Deleted.Count -gt 0) { + if ($RemoveEmptyFolders) { + $FoldersToCheck = $Deleted.DirectoryName | Sort-Object -Unique + foreach ($Folder in $FoldersToCheck) { + $FolderName = $Folder.Substring($Folder.Length - 4) + if ($FolderName -eq '\Adm') { + try { + $MeasureCount = Get-ChildItem -LiteralPath $Folder -Force -ErrorAction Stop | Select-Object -First 1 | Measure-Object + } catch { + Write-Warning "Remove-GPOZaurrLegacyFiles - Couldn't verify if folder $Folder is empty. Skipping. Error: $($_.Exception.Message)." + continue + } + if ($MeasureCount.Count -eq 0) { + Write-Verbose "Remove-GPOZaurrLegacyFiles - Deleting empty folder $($Folder)" + try { + Remove-Item -LiteralPath $Folder -Force -Recurse:$false + } catch { + Write-Warning "Remove-GPOZaurrLegacyFiles - Failed to remove folder $($Folder): $($_.Exception.Message)." + } + } else { + Write-Verbose "Remove-GPOZaurrLegacyFiles - Skipping not empty folder from deletion $($Folder)" + } + } + } } } } \ No newline at end of file diff --git a/Public/Remove-GPOZaurrOrphanedSysvolFolders.ps1 b/Public/Remove-GPOZaurrOrphanedSysvolFolders.ps1 index db74065..e367b9c 100644 --- a/Public/Remove-GPOZaurrOrphanedSysvolFolders.ps1 +++ b/Public/Remove-GPOZaurrOrphanedSysvolFolders.ps1 @@ -10,35 +10,38 @@ [alias('Domain', 'Domains')][string[]] $IncludeDomains, [System.Collections.IDictionary] $ExtendedForestInformation ) - Begin { - if ($BackupPath) { - if ($BackupDated) { - $BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))" - } else { - $BackupFinalPath = $BackupPath - } + if ($BackupPath) { + if ($BackupDated) { + $BackupFinalPath = "$BackupPath\$((Get-Date).ToString('yyyy-MM-dd_HH_mm_ss'))" } else { - $BackupFinalPath = '' + $BackupFinalPath = $BackupPath } + } else { + $BackupFinalPath = '' } - Process { - Get-GPOZaurrSysvol -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation | Where-Object { - if ($_.Status -eq 'Orphaned GPO') { - $_ + Get-GPOZaurrSysvol -Forest $Forest -IncludeDomains $IncludeDomains -ExcludeDomains $ExcludeDomains -ExtendedForestInformation $ExtendedForestInformation | Where-Object { + if ($_.Status -eq 'Orphaned GPO') { + $_ + } + } | Select-Object | Select-Object -First $LimitProcessing | ForEach-Object { + Write-Verbose "Remove-GPOZaurrOrphanedSysvolFolders - Processing $($_.Path)" + if ($BackupFinalPath) { + Try { + Write-Verbose "Remove-GPOZaurrOrphanedSysvolFolders - Backing up $($_.Path)" + Copy-Item -LiteralPath $_.Path -Recurse -Destination $BackupFinalPath -ErrorAction Stop + $BackupWorked = $true + } catch { + Write-Warning "Remove-GPOZaurrOrphanedSysvolFolders - Error backing up error: $($_.Exception.Message)" + $BackupWorked = $false } - } | Select-Object | Select-Object -First $LimitProcessing | ForEach-Object { - Write-Verbose "Remove-GPOZaurrOrphanedSysvolFolders - Processing $($_.Path)" - if ($BackupFinalPath) { - Try { - Copy-Item -LiteralPath $_.Path -Recurse -Destination $BackupFinalPath -ErrorAction Stop - $BackupWorked = $true - } catch { - Write-Warning "Remove-GPOZaurrOrphanedSysvolFolders - Error backing up error: $($_.Exception.Message)" - $BackupWorked = $false - } - } - if ($BackupWorked) { + } + if ($BackupWorked -or $BackupFinalPath -eq '') { + Write-Verbose "Remove-GPOZaurrOrphanedSysvolFolders - Deleting $($_.Path)" + try { Remove-Item -Recurse -Force -LiteralPath $_.Path + } catch { + $ErrorMessage = $_.Exception.Message + Write-Warning "Remove-GPOZaurrOrphanedSysvolFolders - Failed to remove file $($_.Path): $($ErrorMessage)." } } }