Ich habe angefangen ein kleines PowerShell Script zu erstellen, welches dabei unterstützen soll ungültige oder verwaiste Gruppenrichtlinien aufzuspüren. Die erste Version des Scripts sucht nach Gruppenrichtlinien die nicht mit einer OU verbunden sind, keine Sicherheitsfilterung besitzen oder alle Einstellungen der GPO deaktiviert wurden. Das Script werde ich zukünftig noch um weitere Funktionen erweitern und als kleines Tool veröffentlichen. Hier aber erst einmal ein Vorgeschmack.
Das Script gibt den Status “Disabled” aus, wenn alle Einstellungen der Richtlinie deaktiviert wurden, hier ein Beispiel:
Der Status “Not Linked” wird ausgegeben, wenn die Gruppenrichtlinie mit keiner Organisationseinheit (OU) verknüpft ist, wie hier zu sehen:
Wenn die Sicherheitsfilterung leer ist, wird der Status “No Permissions” angegeben:
Das Script erfordert das PowerShell Modul “GroupPolicy”, welches Bestandteil ab Windows Server 2008 R2 ist. Getestet habe ich das Script bisher auf Windows Server 2012 R2, Windows Server 2016 und remote via Windows 10.
So sieht die Ausgabe des Scripts in der PowerShell aus:
Und hier nun das eigentliche Script, welches gerne angepasst oder umgeschrieben werden kann:
$AllGPOs = Get-GPO -All $GPOCount = $AllGPOs.count write-host "" write-host "Found $GPOCount GPOs..." write-host "Analyzing, please wait..." $InvalidGPOs = @() #Disabled GPOs $DisabledGPOs = $AllGPOs | where {$_.GpoStatus -match "AllSettingsDisabled"} foreach ($DisabledGPO in $DisabledGPOs) { $GPOName = $DisabledGPO.Displayname $InvalidGPOs += new-object PSObject -property @{GPOName="$GPOName";State="Disabled"} #write-host "Not active: $GPOName" } #GPO Links / Permissions / Empty GPOs $EnabledGPOs = $AllGPOs | where {$_.GpoStatus -notmatch "AllSettingsDisabled"} foreach ($EnabledGPO in $EnabledGPOs) { $GPOName = $EnabledGPO.Displayname [XML]$GPOReport = Get-GPOReport $GPOName -ReportType XML $GPOLinks = $GPOReport.GPO.LinksTo $GPOApplyPermission = Get-GPPermission $GPOName -All | where {$_.Permission -match "GpoApply"} if ($GPOLinks) { $GPOLinkCount = $GPOLinks.Count $DisabledGPOLinksCount = ($GPOLinks | where {$_.enabled -eq "false"}).Count if ($GPOLinkCount -eq $DisabledGPOLinksCount) { #write-host "All Links disabled: $GPOName" $InvalidGPOs += new-object PSObject -property @{GPOName="$GPOName";State="All Links disabled"} } } if (!$GPOLinks) { $Sitelinked = Get-ADObject -LDAPFilter '(objectClass=site)' -SearchBase "CN=Sites,$((Get-ADRootDSE).configurationNamingContext)" -SearchScope OneLevel -Properties gPLink | Where-Object { $_.gpLink -match $EnabledGPO.Id} if (!$Sitelinked) { #write-host "Not linked: $GPOName" $InvalidGPOs += new-object -TypeName PSObject -Property @{GPOName="$GPOName";State='Not Linked'} } } if (!$GPOApplyPermission) { #write-host "No permissions: $GPOName" $InvalidGPOs += new-object PSObject -property @{GPOName="$GPOName";State="No Permissions"} } if (!$GPOReport.GPO.Computer.ExtensionData -and !$GPOReport.GPO.User.ExtensionData) { #write-host "Empty GPO: $GPOName" $InvalidGPOs += new-object PSObject -property @{GPOName="$GPOName";State="Empty"} } } $InvalidGPOs | sort state | ft GPOName,State $InvalidGPOCount = $InvalidGPOs.Count write-host "Found $InvalidGPOCount invalid GPOs" write-host "" #Optional: delete invalid GPOs foreach ($InvalidGPO in $InvalidGPOs) { Remove-GPO $InvalidGPO.GPOName }
Der untere Teil nach #Optional kann verwendet werden, um die gefundenen ungültigen GPOs zu löschen. Das Objekt “$InvalidGPOs” enthält alle gefunden ungültigen GPOs und lässt sich somit auch weiterverwenden.
Vielleicht nützt es ja jemanden.
Update 15.06.2017: Die aktualisierte Version des Scripts listet nun auch leere Gruppenrichtlinien (ohne Einstellungen) und GPOs auf bei denen alle Verknüpfungen zu OUs deaktiviert sind auf.
Update 01.07.2017: Die aktualisierte Version des Scripts berücksichtigt nun auch GPOs die an AD-Standorte verknüpft wurden. Danke an Torsten für den Hinweis.
Hallo Frank,
danke für dein Script. Funktioniert einwandfrei.
Da ich der absolute Laie bin, würde ich gerne noch wissen wie man das Ergebnis in eine .txt Datei übertragen kann?
Beste Grüße
dogved
Ausgabe angepasst
Zeile 53
von : $InvalidGPOs | sort state | ft GPOName,State
auf $InvalidGPOs | sort state | ft GPOName,State -AutoSize
gruß Georg
Moin, lange her, dass Du Dir diese arbeit hier gemacht hast. Trotzdem: hat mir gerade sehr geholfen! Danke dafür!
Michael
Hallo Frank,
zunächste vielen Dank für die Mühe mit dem Script und natürlich auch deiner Webseite.
Mir ist allerdings ein Unstimmgkeit aufgefallen, wenn es um deaktivierte GPOs geht:
Scheinbar ist die Abfrage bei deaktivierten GPOs etwas „ungenau“, da ich interessanterweise eine GPO angezeigt bekomme, welche mit „All Links disabled“ gekennzeichnet wurde. Schau ich mir die GPO dann über GUI bzw. über ein einfaches: get-gpo -name NameDerGPO an, so wird mir angezeigt das nur die User Settings disabled wurden, nicht aber die gesamte GPO.
Bei allen anderen GPOs wo auch entweder User oder Computer disabled wurde, scheint es keine Ungenauigkeit zu geben und diese werden auch nicht gelistet.
Vielleicht fällt dir dazu ja noch etwas ein, bzw. wenn ich dir weitere Infos liefern soll, einfach raus damit.
Danke und Gruß,
Eric
Hallo Eric,
danke für den Hinweis. Ich schaue es mir an.
Gruß, Frank
Hallo Frank,
erstmal vielen Dank für das nützliche Skript :-)
Bei genauerer Betrachtung ist mir jedoch noch eine unschöne Sache aufgefallen.
Falls ein GPO auf eine AD-Site verknüpft wurde, wird dies im GPOReport nicht angezeigt und somit vom Skript als nicht verknüpft gewertet.
Beim Aktivieren des optionalen Löschens kann dies damit zu unschönen Effekten führen!!!
Da der GPOReport die Verlinkung zum Standort nicht anzeigt, kannst Du dies durch eine zusätzliche kleine LDAP Abfrage abfangen:
if (!$GPOLinks)
{
$Sitelinked = Get-ADObject -LDAPFilter ‚(objectClass=site)‘ -SearchBase „CN=Sites,$((Get-ADRootDSE).configurationNamingContext)“ -SearchScope OneLevel -Properties gPLink | Where-Object { $_.gpLink -match $EnabledGPO.Id}
if (!$Sitelinked)
{
Write-Output -inputObject „Not linked: $GPOName“
$InvalidGPOs += new-object -TypeName PSObject -Property @{GPOName=“$GPOName“;State=’Not Linked‘}
}
}
Happy scripting,
Torsten
Sehr schönes Skript!
Sofern es noch weiterentwickelt wird, wäre es hilfreich, wenn das Skript auch verknüpfte GPOs erkennt, die Verknüpfungen allerdings deaktiviert sind. Das scheint aktuell noch nicht der Fall zu sein.
Hi Sven,
danke für den Hinweis. Ich habe schon eine neue Version die ich morgen noch einmal kurz testen werde. Das neue Script erkennt dann auch leere GPOs und, wie von dir vorgeschlagen, GPOs bei denen alle Verknüpfungen deaktiviert sind.
Gruß, Frank
Hi Sven,
die aktualisierte Version findet nun auch leere GPOs und zusätzlich auch GPOs bei denen alle Verknüpfungen deaktiviert sind.
Gruß,
Frank
:-)
Vorm schreiben Website aktualsieren…..
Hallo,
ich musste bei mir
Get-GPPermission
ersetzen mit
Get-GPPermissions
Windows 2008R2
Hi Jörg,
ab Server 2012 R2 (zumindest gerade ausprobiert), ist Get-GPPermissions nur ein Alias für Get-GPPermissions:
DisplayName : Get-GPPermissions
CommandType : Alias
Definition : Get-GPPermission
Mag sein, dass es bei Server 2008 R2 noch anders ist.
Gruß, Frank
Hallo Frank,
Danke für das Powershell-Skript. Was mich schon lange nervt, sind das ich die alten klassischen administrativen Vorlagen (ADM) löschen möchte. In der Gruppenrichtlinienverwaltung, habe ich GPO von Word/Excel/Powerpoint/Outlook 2000, 2003, 2007, 2010 etc.
Gibt es eine Möglichkeit das man die alten ADM-Vorlagen (inkl. allenfalls vorhandenen Einstellungen) löschen kann? Das würde die Übersicht massiv verbessern und allenfalls auch fehlerhafte Einstellungen bereinigen…
Ich bekomme auf einem Server 2012R2 beim ausführen folgenden Fehler:
In C:\Search-InvalidGPOs.ps1:15 Zeichen:69
+ $InvalidGPOs += new-object PSObject -property @{GPOName=“$GPOName“State=“Disab …
+ ~~~~~~~~~~~~
Unerwartetes Token „State=“Disabled““ in Ausdruck oder Anweisung.
In C:\Search-InvalidGPOs.ps1:15 Zeichen:69
+ $InvalidGPOs += new-object PSObject -property @{GPOName=“$GPOName“State=“Disab …
+ ~
Das Hashliteral war unvollständig.
In C:\Search-InvalidGPOs.ps1:17 Zeichen:2
+ }
+ ~
Unerwartetes Token „}“ in Ausdruck oder Anweisung.
In C:\Search-InvalidGPOs.ps1:31 Zeichen:71
+ $InvalidGPOs += new-object PSObject -property @{GPOName=“$GPOName“State=“Not …
+ ~~~~~~~~~~
Unerwartetes Token „State=“Not Linked““ in Ausdruck oder Anweisung.
In C:\Search-InvalidGPOs.ps1:31 Zeichen:71
+ $InvalidGPOs += new-object PSObject -property @{GPOName=“$GPOName“State=“Not …
+ ~
Das Hashliteral war unvollständig.
In C:\Search-InvalidGPOs.ps1:37 Zeichen:71
+ $InvalidGPOs += new-object PSObject -property @{GPOName=“$GPOName“State=“No …
+ ~~~~~~~~~~
Unerwartetes Token „State=“No Permissions““ in Ausdruck oder Anweisung.
In C:\Search-InvalidGPOs.ps1:37 Zeichen:71
+ $InvalidGPOs += new-object PSObject -property @{GPOName=“$GPOName“State=“No …
+ ~
Das Hashliteral war unvollständig.
In C:\Search-InvalidGPOs.ps1:38 Zeichen:4
+ }
+ ~
Unerwartetes Token „}“ in Ausdruck oder Anweisung.
In C:\Search-InvalidGPOs.ps1:39 Zeichen:2
+ }
+ ~
Unerwartetes Token „}“ in Ausdruck oder Anweisung.
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : UnexpectedToken
Hallo Frank
Im Script gibt’s noch einen Fehler:
Statt „Get-PPermissions“ steht an einer Stelle „Get-PPermission“