Inhalt

Gruppenrichtlinien mit PowerShell verwalten

Trotz Intune, DSC und Entra ID (Azure AD); Gruppenrichtlinien sind in vielen Firmen immer noch ein weit verbreitete Methode um Windows Clients und Server zu verwalten. Die MMC ist aber nicht wirklich dazu geeignet mehrere hundert GPOs zu verwalten. Doch dafür gibt es PowerShell.

Die meisten werden Get-GPO schon genutzt haben, aber Microsoft liefert noch 25 weitere cmdlets und zwei Alias im Modul GroupPolicy.

PS C:\Users\Fabian> Get-Command -Module GroupPolicy
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Alias           Get-GPPermissions                                  1.0.0.0    GroupPolicy
Alias           Set-GPPermissions                                  1.0.0.0    GroupPolicy
Cmdlet          Backup-GPO                                         1.0.0.0    GroupPolicy
Cmdlet          Copy-GPO                                           1.0.0.0    GroupPolicy
Cmdlet          Get-GPInheritance                                  1.0.0.0    GroupPolicy
Cmdlet          Get-GPO                                            1.0.0.0    GroupPolicy
Cmdlet          Get-GPOReport                                      1.0.0.0    GroupPolicy
Cmdlet          Get-GPPermission                                   1.0.0.0    GroupPolicy
Cmdlet          Get-GPPrefRegistryValue                            1.0.0.0    GroupPolicy
Cmdlet          Get-GPRegistryValue                                1.0.0.0    GroupPolicy
Cmdlet          Get-GPResultantSetOfPolicy                         1.0.0.0    GroupPolicy
Cmdlet          Get-GPStarterGPO                                   1.0.0.0    GroupPolicy
Cmdlet          Import-GPO                                         1.0.0.0    GroupPolicy
Cmdlet          Invoke-GPUpdate                                    1.0.0.0    GroupPolicy
Cmdlet          New-GPLink                                         1.0.0.0    GroupPolicy
Cmdlet          New-GPO                                            1.0.0.0    GroupPolicy
Cmdlet          New-GPStarterGPO                                   1.0.0.0    GroupPolicy
Cmdlet          Remove-GPLink                                      1.0.0.0    GroupPolicy
Cmdlet          Remove-GPO                                         1.0.0.0    GroupPolicy
Cmdlet          Remove-GPPrefRegistryValue                         1.0.0.0    GroupPolicy
Cmdlet          Remove-GPRegistryValue                             1.0.0.0    GroupPolicy
Cmdlet          Rename-GPO                                         1.0.0.0    GroupPolicy
Cmdlet          Restore-GPO                                        1.0.0.0    GroupPolicy
Cmdlet          Set-GPInheritance                                  1.0.0.0    GroupPolicy
Cmdlet          Set-GPLink                                         1.0.0.0    GroupPolicy
Cmdlet          Set-GPPermission                                   1.0.0.0    GroupPolicy
Cmdlet          Set-GPPrefRegistryValue                            1.0.0.0    GroupPolicy
Cmdlet          Set-GPRegistryValue                                1.0.0.0    GroupPolicy

Gruppenrichtlinien finden

Leider unterstützt Get-Gpo keinerlei Suchfilter sondern kann nur alle oder eine GPO ausgeben. Dabei kann man über -Id oder -Name einzelne Gruppenrichtlinien angeben, muss aber die Werte exakt kennen. Mit dem Parameter -All wird eine Liste aller GPOs ausgeben.

Eine Filter kann daher immer erst im Nachgang angewendet werden.

# Default Domain Policy
Get-Gpo -Id 31b2f340-016d-11d2-945f-00c04fb984f9

# Default Domain Controllers Policy
Get-Gpo -Id 6ac1786c-016f-11d2-945f-00c04fb984f9

# List all GPOs
Get-Gpo -All

# Filter by name
Get-Gpo -All | Where-Object DisplayName -match "Default Domain"
Info
Die Default Domain Policy und die Default Domain Controller Policy haben immer exakt diese GUIDs.

Gruppenrichtlinien erstellen

Um eine neue, leere Gruppenrichtlinie anzulegen muss man nicht viel Informationen liefern. Ein sprechender Name ist ausreichend, ein beschreibender Kommentar wünschenswert.

PS C:\Users\Fabian> New-GPO -Name "MSFT Windows Server 2022 - Member Server" -Comment "Microsoft Security Baseline"
DisplayName      : MSFT Windows Server 2022 - Member Server
DomainName       : lab.bader.cloud
Owner            : LAB\Domain Admins
Id               : 0ef52ff6-0ef0-42e1-9273-508463a69a44
GpoStatus        : AllSettingsEnabled
Description      : Microsoft Security Baseline
CreationTime     : 11/15/2021 8:07:55 PM
ModificationTime : 11/15/2021 8:07:55 PM
UserVersion      : AD Version: 0, SysVol Version: 0
ComputerVersion  : AD Version: 0, SysVol Version: 0
WmiFilter        :

Einstellungen importieren

Bei z.B. Security Baselines werden häufig Exporte von Gruppenrichtlinien als Vorlagen geliefert. So macht es auch Microsoft mit Ihrem Microsoft Security Compliance Toolkit. Dieses enthält für alle gängigen Betriebssysteme und Browser die von Microsoft empfohlenen Einstellungen.

Um diese Vorlagen zu importieren kann Import-GPO genutzt werden. Dieses cmdlet überschreibt alle in der Gruppenrichtlinie vorhandenen Einstellungen

$GPO = Get-Gpo -Name "MSFT Windows Server 2022 - Member Server"
Import-GPO -Path "C:\Users\Fabian\Downloads\Windows Server-2022-Security-Baseline-FINAL\GPOs" -BackupGpoName "MSFT Windows Server 2022 - Member Server" -TargetGuid $GPO.id

Anstatt die GPO schon vorher anzulegen kann dies mit dem Parameter -CreateIfNeeded in einem Schritt gemacht werden.

Import-GPO -Path "C:\Users\Fabian\Downloads\Windows Server-2022-Security-Baseline-FINAL\GPOs" -BackupGpoName "MSFT Windows Server 2022 - Domain Controller" -TargetName "MSFT Windows Server 2022 - Domain Controller" -CreateIfNeeded

Beim Import einer GPO kann auch eine Mapping Datei angegeben werden. Diese übersetzt z.B. SIDs von einer Domäne in die andere.

Import-GPO -Path "C:\Users\Fabian\Downloads\Windows Server-2022-Security-Baseline-FINAL\GPOs" -BackupGpoName "MSFT Windows Server 2022 - Domain Controller" -TargetName "MSFT Windows Server 2022 - Domain Controller" -CreateIfNeeded -MigrationTable C:\Users\Fabian\Documents\DomainA2DomainB.migtable

Die benötigte .migtable Datei muss dabei manuell erstellt werden. Hier empfiehlt es sich eventuell doch einmal die GUI zu nutzen und das entsprechende Mapping für die zukünftige Nutzung zu speichern. Wer darauf keine Lust hat kann die Datei auch leicht von Hand erstellen. Es handelt sich um eine XML Datei wie in diesem Beispiel zu sehen.

<?xml version="1.0" encoding="utf-16"?>
<MigrationTable xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.microsoft.com/GroupPolicy/GPOOperations/MigrationTable">
  <Mapping>
    <Type>Computer</Type>
    <Source>COMPUTER01</Source>
    <Destination>COMPUTER02</Destination>
  </Mapping>
  <Mapping>
    <Type>User</Type>
    <Source>Administrator</Source>
    <Destination>SuperAdmin</Destination>
  </Mapping>
</MigrationTable>

Report erstellen

Ähnlich wie in der GUI kann man sich die Konfiguration einer Gruppenrichtlinie ausgeben lassen. Dabei können jedoch unterschiedliche Formate gewählt werden.

  • HTML = Der aus der MMC bekannt Report der im Browser angesehen werden kann
  • XML = Ausgabe im XML Formart. Dies ermöglicht es z.B. die Informationen mittels PowerShell weiter zu verarbeiten.
Get-GPOReport -Name "MSFT Windows Server 2022 - Member Server" -ReportType Html -Path .\Desktop\Report.html
Get-GPOReport -Name "MSFT Windows Server 2022 - Member Server" -ReportType Xml -Path .\Desktop\Report.xml
Info
Get-GPOReport ist leider nicht sehr performant, weswegen ich das cmdlet nicht für Auswertungen über viele Gruppenrichtlinien empfehlen kann.

Damit eine GPO überhaupt erst angewendet wird, muss diese mit einer oder mehrerer OUs, Domänen oder Sites verknüpft werden. New-GPLink erledigt dies zuverlässig. Will man anschließend diese Verknüpfung anpassen ist dies mit Set-GPLink leicht möglich. Das erleichtert z.B. die Anpassung der Link Order ungemein.

$GPO = Get-Gpo -Name "MSFT Windows Server 2022 - Member Server"
# Create a new link
New-GPLink -Guid $GPO.Id -Target "OU=DemoOU,$((Get-ADDomain).DistinguishedName)" -LinkEnabled Yes -Order 1
# Change existing link
Set-GPLink -Guid $GPO.Id -Target "OU=DemoOU,$((Get-ADDomain).DistinguishedName)" -LinkEnabled No
Info
Mit dem Parameter Enforced wird die Einstellungen dieser Gruppenrichtlinie immer angewendet unabhängig von der “Link Order” oder unterbrochener Vererbung.
Set-GPLink -Guid $GPO.Id -Target "OU=DemoOU,$((Get-ADDomain).DistinguishedName)" -Enforced Yes

Ebenso einfach lässt sich eine solche Verknüpfung wieder entfernen.

Remove-GPLink -Guid $GPO.Id -Target "OU=DemoOU,$((Get-ADDomain).DistinguishedName)"

Zugewiesene Gruppenrichtlinien ermitteln

Um herauszufinden welche GPO an einer bestimmten OU überhaupt wirken wird Get-GPInheritance genutzt.

Get-GPInheritance "OU=DemoOU,$((Get-ADDomain).DistinguishedName)"
Name                  : demoou
ContainerType         : OU
Path                  : ou=demoou,dc=lab,dc=bader,dc=cloud
GpoInheritanceBlocked : No
GpoLinks              : {}
InheritedGpoLinks     : {Default Domain Policy}

Vererbung unterbrechen

Set-GPInheritance "OU=DemoOU,$((Get-ADDomain).DistinguishedName)" -IsBlocked Yes
Name                  : demoou
ContainerType         : OU
Path                  : ou=demoou,dc=lab,dc=bader,dc=cloud
GpoInheritanceBlocked : Yes
GpoLinks              : {}
InheritedGpoLinks     : {}
Info
Eine in der Hierarchie weiter oben verknüpfte GPO bei der das Flag Enforced aktiviert ist, wirkt auch wenn die Vererbung unterbrochen wird auf die Objekte unterhalb der OU.

WMI Filter zuweisen

Das Erstellen von WMI Filtern ist leider über die PowerShell nicht mit nativen cmdlets möglich. Natürlich hat die Community nachgelegt und ein entsprechendes Modul zu Verfügung gestellt. GPWmiFilter von Friedrich Weinmann.

Was nativ jedoch möglich ist, ist es wenn ein WMI schon in Verwendung ist, diesen einer weiteren GPO zuzuweisen.

# Get WMI filter from existing GPO
$WMIFilter = Get-GPO -Name "SourceGPO" | Select-Object -ExpandProperty WmiFilter
# Get target GPO ...
$GPO = Get-GPO -Name "MSFT Windows Server 2022 - Member Server"
# ... an apply the WMI filter
$GPO.WmiFilter = $WMIFilter

Berechtigungen auslesen

Um die Berechtigungen auszulesen die einer Gruppenrichtlinie zugewiesen sind, sollte Get-GPPermission genutzt werden. Auch hier ist wieder möglich alle auszulesen, oder nur für eine benannte Gruppe oder AD Objekt.

Get-GPPermission -Name "MSFT Windows Server 2022 - Member Server" -All
Trustee     : Authenticated Users
TrusteeType : WellKnownGroup
Permission  : GpoApply
Inherited   : False

Trustee     : Domain Admins
TrusteeType : Group
Permission  : GpoEditDeleteModifySecurity
Inherited   : False

Trustee     : Enterprise Admins
TrusteeType : Group
Permission  : GpoEditDeleteModifySecurity
Inherited   : False

Trustee     : ENTERPRISE DOMAIN CONTROLLERS
TrusteeType : WellKnownGroup
Permission  : GpoRead
Inherited   : False

Trustee     : SYSTEM
TrusteeType : WellKnownGroup
Permission  : GpoEditDeleteModifySecurity
Inherited   : False
Get-GPPermission -Name "MSFT Windows Server 2022 - Member Server" -TargetType Group
Trustee     : Authenticated Users
TrusteeType : WellKnownGroup
Permission  : GpoApply
Inherited   : False

Berechtigungen setzen

Soll eine Gruppenrichtlinie nur einer speziellen Gruppe an Personen zugewiesen werden muss z.B. immer die Berechtigung der Authenticated Users verändert werden. Diese dürfen unter keinen Umständen einfach entfernt werden.

Der Standardmodus des cmdlets Set-GPPermission ist additiv, die benannten Rechte werden hinzugefügt. Sollten schon höhere Rechte vorhanden sein, bleiben diese bestehen. Daher ist die korrekte Verwendung des Parameters -Replace wichtig.

$GPO = Get-Gpo -Name "MSFT Windows 11 - User"
Set-GPPermission -Guid $GPO.Id -PermissionLevel GpoApply -TargetType Group -TargetName "Windows 11 Users"
Set-GPPermission -Name $GPO.Id -PermissionLevel GpoRead -TargetType Group -TargetName "Authenticated Users" -Replace

Dasselbe cmdlet wird auch verwendet wenn Berechtigungen entfernt werden sollen. Hier wird als PermissionLevel None angegeben und der Parameter -Replace genutzt.

Set-GPPermission -Guid $GPO.Id -PermissionLevel None -TargetName "Windows 11 Users" -TargetType Group -Replace

Backup

Eine Sicherung der Gruppenrichtlinien ist mit Backup-GPO sehr einfach. Es wird für jedes Backup ein neues Verzeichnis mit einer GUID erstellt und alle relevanten Daten in diesem Verzeichnis gespeichert. Eine regelmäßige Sicherung aller GPOs ist damit kein Problem.

Get-GPO -All | Backup-GPO -Path C:\GPOBackup\ -Comment "$(Get-Date)"

Werte auslesen

Um einzelne Werte aus einer GPO auszulesen gibt es zwei cmdlets: Get-GPPrefRegistryValue und Get-GPRegistryValue.

Der Unterschied der beiden cmdlets besteht darin, Art von Registry Wert sie aus der Gruppenrichtlinie auslesen.

Get-GPPrefRegistryValue liest Werte aus, die unterhalb von Computer -> Preferences -> Windows Settings -> Registry angelegt wurden. Dies sind nicht die Werte die in der GUI über ADMX Dateien gesteuert werden, sondern Werte die man manuell hinzufügt. Sie sind nicht auf HKEY_LOCAL_(MACHINE|USER)\SOFTWARE\Policies\ beschränkt.

Get-GPRegistryValue ist alles was über eine ADMX Datei in der registry.pol gespeichert wird. Dies ist in den meisten Fällen der Bereich den man auch bearbeiten möchte.

Unter Angabe des Registry -Key können alle Werte in diesem ausgelesen werden.

Get-GPRegistryValue -Guid $GPO.Id -Key "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service\"
KeyPath     : SOFTWARE\Policies\Microsoft\Windows\WinRM\Service
FullKeyPath : HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service
Hive        : LocalMachine
PolicyState : Set
Value       : 0
Type        : DWord
ValueName   : AllowBasic
HasValue    : True

KeyPath     : SOFTWARE\Policies\Microsoft\Windows\WinRM\Service
FullKeyPath : HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service
Hive        : LocalMachine
PolicyState : Set
Value       : 0
Type        : DWord
ValueName   : AllowUnencryptedTraffic
HasValue    : True

KeyPath     : SOFTWARE\Policies\Microsoft\Windows\WinRM\Service
FullKeyPath : HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service
Hive        : LocalMachine
PolicyState : Set
Value       : 1
Type        : DWord
ValueName   : DisableRunAs
HasValue    : True
Get-GPRegistryValue -Guid $GPO.Id -Key "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service\" -ValueName AllowBasic
KeyPath     : SOFTWARE\Policies\Microsoft\Windows\WinRM\Service
FullKeyPath : HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WinRM\Service
Hive        : LocalMachine
PolicyState : Set
Value       : 0
Type        : DWord
ValueName   : AllowBasic
HasValue    : True
Info
Audit und SecEdit sind zwei Bereiche der Gruppenrichtlinien die man über native cmdlets nicht auslesen oder bearbeiten kann. Da es sich hierbei aber um Textdateien handelt, ist die aber grundsätzlich möglich.

Werte setzen

Mittels Set-GPRegistryValue lassen sich einzelne Werte in der GPO auch verändern. Somit ist zum setzen von neuen Einstellungen nicht mehr die MMC notwendig, sondern nur noch das Wissen in welchem Registry Key sich welche Einstellung verbirgt.

Hierbei ist die Webseite admx.help eine große Hilfe. Für fast alle ADMX Dateien werden die entsprechenden Werte aufbereitet dargestellt und die notwendigen Informationen zu “Registry Path” und Value Name/Type und möglichen Werten aufgeschlüsselt.

Hier am Beispiel der Einstellung Turn off real-time protection

Set-GPRegistryValue -Name 'MSFT Windows Server 2022 - Member Server' -Key 'HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows Defender\Real-Time Protection' -ValueName 'DisableRealtimeMonitoring' -Type DWord -Value 0

Werte recursive auslesen

Da es mit dem nativen cmdlet Get-GPRegistryValue nicht möglich ist mehr als eine Ebene auszulesen, habe ich inspiriert von diesem Snippet eine Funktion erstellt die dies erlaubt. So können alle Einstellungen einer Gruppenrichtlinie als PolicyRegistrySetting Objekte auszugeben und direkt weiterverarbeitet werden. Dies ist um ein vielfaches schneller als das cmdlet Get-GPOReport.

function Get-GPRegistryValueRecurse {
    [CmdletBinding()]
    param (
        [Alias('DisplayName')]
        [Parameter(Mandatory = $true,
            ValueFromPipelineByPropertyName = $true)]
        [string]$Name,

        [Parameter(Mandatory = $true)]
        [string]$Key
    )
    
    # Remove trailing backslash
    $Key = $Key -replace '\\$'
    $RegistryItems = Get-GPRegistryValue -Name $Name -Key $key
    Write-Verbose "Found $($RegistryItems.Count) items"
    foreach ($RegistryItem in $RegistryItems) {
        if ( $RegistryItem -is [Microsoft.GroupPolicy.PolicyRegistrySetting] ) {
            # Output the registry item
            $RegistryItem
            Write-Verbose "Found registry item: $($RegistryItem.Name)"
        } elseif ( $RegistryItem -is [Microsoft.GroupPolicy.RegistryItem]) {
            Write-Verbose "Found registry key: $($RegistryItem.FullKeyPath)"
            # Go deeper
            Get-GPRegistryValueRecurse -Key $RegistryItem.FullKeyPath -Name $Name
        }
    }
}

Beispielhafte Anwendung mit Pipelining

$GPO | Get-GPRegistryValueRecurse -Key "HKLM\SOFTWARE\Policies\Microsoft\Windows\WinRM\Client\"

Gruppenrichtlinien zusammenführen

Mit dem eben Gelernten lässt sich auch ein weiteres Problem leicht lösen: Wie kann man zwei Gruppenrichtlinien in zusammenführen?

Da die GUI nur den Import anbietet, diese Funktion aber alle bestehenden Einstellungen überschreibt bleibt auch hier nur der Griff zur PowerShell.

Im folgenden Beispiel lese ich erst rekursiv alle Werte unterhalb des Registry Keys HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\ aus, um diese anschließend mittels Set-GPRegistryValue in die schon bestehenden GPO “MSFT Windows Server 2022 - Member Server” zu übernehmen.

# Retrieve all settings from a specific registry key
$SourceSettings = Get-GPRegistryValueRecurse -Name "MSFT Windows Server 2022 - Defender Antivirus" -Key "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\"
# Define target GPO name
$TargetGPOName = "MSFT Windows Server 2022 - Member Server"
foreach ($Setting in $SourceSettings) {
    if (-not [string]::isNullOrWhitespace($Setting ) ) {
        # Replace double backslash if present
        $TargetKey = $Setting.FullKeyPath -replace '\\\\', '\'
        if ($Setting.PolicyState -eq "Delete") {
            Set-GPRegistryValue -Name $TargetGPOName -Key $TargetKey -ValueName $($Setting.ValueName) -Disable
        } else {
            Set-GPRegistryValue -Name $TargetGPOName -Key $TargetKey -ValueName $($Setting.ValueName) -Type $($Setting.Type) -Value $($Setting.Value)
        }
    }
}
Warnung
Auch hier gilt das die Quell GPO schon vorhandene Werte in der Ziel GPO überschreibt.
Jedoch nicht alle Werte, sondern nur Werte die in Konflikt mit der Quell GPO stehen.

Dieses Beispiel lässt sich sehr leicht anpassen um alle “Computer” Werte zu übernehmen.

$SourceSettings = Get-GPRegistryValueRecurse -Name "MSFT Windows Server 2022 - Defender Antivirus" -Key "HKLM\SOFTWARE\"

Firewalleinstellungen bearbeiten

Um die Windows Defender Firewall Einstellungen bearbeiten zu können taugt keiner der oben genannten Befehle. Jedoch hat Microsoft noch zwei weitere cmdlets, besser gesagt Function, im Petto.

PS C:\Users\Fabian> Get-Command *gpo*  | ? Source -ne GroupPolicy
CommandType     Name                                               Version    Source
-----------     ----                                               -------    ------
Function        Open-NetGPO                                        2.0.0.0    NetSecurity
Function        Save-NetGPO                                        2.0.0.0    NetSecurity
Application     chgport.exe                                        10.0.22... C:\Windows\system32\chgport.exe

Open-NetGPO erstellt eine WMI Session zu einer bestehenden Gruppenrichtlinie und erlaubt es dann, diese mit den normalen cmdlets um Firewallregeln zu verwalten zu nutzen. Die WMI Session muss dabei immer an den Parameter -GPOSession übergeben werden.

Info
Aus mir unerfindlichen Gründen ist der Parameter -GPOSession in der Windows 11 von Get-NetFirewallRule nicht mehr vorhanden. Hier kann alternativ -PolicyStore genutzt werden.
New-GPO -Name "GPO_Server_Windows_Firewall"
$gpoSession = Open-NetGPO -PolicyStore lab.bader.cloud\GPO_Server_Windows_Firewall
# Windows until 11
Get-NetFirewallRule -GPOSession $gpoSession
# Windows 11
Get-NetFirewallRule -PolicyStore $gpoSession

# Add outbound DNS queries to 8.8.8.8 
New-NetFirewallRule -GPOSession $gpoSession -DisplayName "Allow traffic to Google" -Action Allow  -Direction Outbound -RemoteAddress 8.8.8.8 -Protocol Udp -RemotePort 53
# Save changes to GPO
Save-NetGPO -GPOSession $gpoSession

# Check if changes where made correctly
$gpoSession = Open-NetGPO -PolicyStore lab.bader.cloud\GPO_Server_Windows_Firewall
Get-NetFirewallRule -PolicyStore $gpoSession

Auswertungen über alle GPOs

Mir kam immer wieder folgende Problemstellung unter: In welcher Gruppenrichtlinie ist einer oder mehrere Einstellungen gesetzt und wenn ja mit welchem Wert.

Die einzige Antwort die ich bisher im Internet gefunden habe, war die Nutzung von Get-GPOReport mit der Umwandlung als XML und anschließender Auswertung der XML Dateien. Leider ist das sehr sehr langsam, bei mehreren hundert, vielleicht sogar tausenden GPOs in der Umgebung ist dies keine echte Lösung.

Daher habe ich das Skript CheckGPOSettings.ps1 erstellt.

Dieses Skript analysiert anhand einer übergebenen Hashtable alle Gruppenrichtlinien in einer oder auch mehrere Domains nach einem oder mehrere Werte und liefert die Ergebnisse als PowerShell Objekt zurück. Dadurch können diese sehr einfach weiter verarbeitet werden. Und das Beste ist, es werden alle typischen Speicherorte innerhalb der GPO durchsucht. Egal ob Get-GPRegistryValue, GptTmpl.inf oder Get-GPPrefRegistryValue, das Script findet sie alle.

Info
In der aktuellen Version werden Audit-Einstellungen aus der audit.csv nicht unterstützt.

Query Hashtable

Die Hashtable die für die Suche genutzt wird, besteht aus zwei verschachtelten Hashtables. Die äußere nutzt als Key einen frei wählbaren Namen für den gesuchten Wert. Hier sollte z.B. der sprechende Name der Einstellung genutzt werden.

Als Value wird ein weiteres Hashtable verwendet, dieses beschreibt den zu suchenden Wert. Als Key wird hier der Registry Key genutzt und als Value den Value Name des Registry Items.

    $CheckSettings = @{
        "Unique setting name for the report. Choose something you can remember like the display name of the setting" = @{
            # Key = The path to the registry key
            "Key"       = "HKLM\SOFTWARE\Policies\Microsoft\PassportForWork"
            # ValueName = The name of the registry value 
            "ValueName" = "Enabled"
        }
    }

In folgendem Beispiel suche ich zwei verschiedene Registry Settings. Das erste ist das LAN Manager Authentication Level (steht hoffentlich auf 5) und das zweite ist der Wert für den Service MrxSmb10 besser bekannt als SMBv1. hier ist als Wert 4 wünschenswert (Deaktiviert).

$CheckSettings = @{
    "Network security: LAN Manager authentication level" = @{
        "Key"       = "HKLM\System\CurrentControlSet\Control\Lsa"
        "ValueName" = "LmCompatibilityLevel"
    }
    "SMBv1 service state" = @{
        "Key"       = "HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MrxSmb10"
        "ValueName" = "Start"
    }

}

Anschließend starte ich die Funktion Get-SettingsFromGPOs und übergebe die zu prüfenden Domain und das eben erstelle Hashtable.

C:\Users\Fabian> Get-SettingsFromGPOs -Domain "lab.bader.cloud" -CheckSetting $CheckSettings

Als Ergebnis bekomme ich folgenden Informationen geliefert:

  • In welcher Domäne ist die Gruppenrichtlinie vorhanden
  • Wie lautet der Name der Gruppenrichtlinie
  • Wo ist diese Gruppenrichtlinie verknüpft*
  • Angewendeten WMI Filter
  • Den Pfad zur GPO
  • aktueller Status der GPO (Enabled/Disabled)
  • Den selbstgewählten Namen für die Einstellung
  • Den Registry Key
  • Den Namen des RegistryItem
  • Den Wert des RegistryItem
  • Den Typ des RegistryItem
DomainName  : lab.bader.cloud
GPO         : MSFT Windows Server 2022 - Domain Controller
GPOLinks    :
Setting     : Network security: LAN Manager authentication level
ValueName   : LmCompatibilityLevel
Hive        : HKLM\System\CurrentControlSet\Control\Lsa
WmiFilter   :
PolicyState :
Value       : 5
Type        : REG_DWORD
Path        : cn={1C09EB8B-1EB0-488B-A92C-34030213F58B},cn=policies,cn=system,DC=lab,DC=bader,DC=cloud

DomainName  : lab.bader.cloud
GPO         : MSFT Windows Server 2022 - Domain Controller
GPOLinks    :
Setting     : SMBv1 service state
ValueName   : Start
Hive        : HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\MrxSmb10
WmiFilter   :
PolicyState : Set
Value       : 4
Type        : DWord
Path        : cn={1C09EB8B-1EB0-488B-A92C-34030213F58B},cn=policies,cn=system,DC=lab,DC=bader,DC=cloud

* Die GPLinks kann ich aktuell nur unter Verwendung des cmdlets Get-GPReport herausfinden. Dieses wird aber immer nur dann gestartet, wenn der gesuchte Wert in der Gruppenrichtlinie gefunden wurde. Für zusätzliche Performance habe ich den Schalten -NoGPLink eingebaut. Dieser hat in meiner kleinen Demoumgebung die Ausführungszeit von 776ms auf 568ms verbessert. In besonders großen Umgebungen kann sich das also lohnen.

In diesem diesem GitHub Repository findet Ihr weitere Beispiele und das Repository steht für Pull Requests offen. So kann über die Zeit eine Sammlung an Suchparametern zusammengetragen werden.

Fazit

Mit der PowerShell lassen sich Gruppenrichtlinien sehr umfangreich manipulieren und wie so häufig spart dies viel Zeit. Leider ist der Einstieg nicht ganz so einfach wie bei anderen cmdlets, aber wenn man das Grundkonzept durchschaut hat lassen sich damit viele tolle Sache realisieren.

Wer genau aufgepasst hat, wird bemerkt haben das es bestimmte cmdlets nicht in den Blogartikel geschafft haben. Das hat entweder den Grund das diese selbsterklärend sind (Copy-GPO), der Gegenpart vorgestellt wurde (Remove-GPRegistryValue) oder ich diese einfach nicht nutze (*-GPStarterGPO).

Daher fehlen Beispiele für folgende cmdlets

  • Copy-GPO
  • Get-GPStarterGPO
  • Invoke-GPUpdate
  • New-GPStarterGPO
  • Remove-GPPrefRegistryValue
  • Remove-GPRegistryValue
  • Rename-GPO
  • Restore-GPO