Build reports and analyze group policies using PowerShell
The topic of using PowerShell for administration is extremely relevant, and more and more articles on this topic appear on Habré. Jeffrey Hicks’s previous translation of the article , which we published last Friday, sparked a wave of interest. And how can one not recall the remarkable performance of the same author at TechEd North America 2012. The report that Jeffrey Hicks conducted with Jeremy Moskowitz was devoted to the analysis of group policy objects and the generation of reports. The original material (video) is here , but we give briefly the content + scripts. In any case, we recommend watching the video itself. The report focused on two issues:
- Builds Group Policy Reports
- Group Policy Analysis
Details - under the cut.
We use PowerShell in conjunction with group policies: what is needed for this?
To use PowerShell when working with group policies, we need:
- Windows 7 (to launch PowerShell)
- RSAT for Windows 7
- Actually PowerShell v2.0 or higher
- Optional: Microsoft Active Directory Provider recommended
The sequence of our actions:
- Import Group Policy Module (Import-module GroupPolicy)
- Get a GPO using PowerShell (Get a GPO PowerShell Object)
- Build a report based on this object
- Building HTML / XML Reports on Group Policy Objects
- Parsim and search in XML for analysis purposes
- Search using Select-XML and Xpath
Build Group Policy Reports
Problem number 1. You need to get information about what is currently happening in group policies
PS C:\> Import-Module GroupPolicy
PS C:\> Get-GPO JeremyGPO
In this case, instead of JeremyGPO stands the display name (DisplayName) of the GPO. This plate is displayed
DisplayName : JeremyGPO
DomainName : GLOBOMANTICS.local
Owner : GLOBOMANTICS\Domain Admins
Id : cd73c562-5bfe-40e2-b81e-28da10da425c
GpoStatus : ComputerSettingsDisabled
Description :
CreationTime : 12/28/2011 2:52:37 PM
ModificationTime : 5/21/2012 11:08:26 AM
UserVersion : AD Version: 4, SysVol Version: 4
ComputerVersion : AD Version: 1, SysVol Version: 1
WmiFilter :
Solution: create reports.
For example, we are faced with the task of obtaining a list of all group objects that have been changed in the last 30 days, sorted in descending order (last at the top) with three values (display name, time of change, description). The received information must be exported to a .csv file (GPOModReport.csv in this example). How it looks in PowerShell:
PS C:\> get-gpo -all | Where {$_.ModificationTime -gt (Get-Date).AddDays(-30)}
... | Sort ModificationTime -Descending | Where {$_.ModificationTime -ge (Get-Date).AddDays(-30)} | Select Displayname,ModificationTime,Description
... | Export-CSV R:\GPOModReport.csv
Examples of additional commands
#Созданные и измененные объекты групповых политик
PS C:\>get-gpo -all | Sort CreationTime,Modificationtime | Select Displayname,*Time
#Находим все объекты групповых политик, измененные за последние 30 дней
PS C:\>get-gpo -all | Where {$_.ModificationTime -ge (Get-Date).AddDays(-30)}
#Создаем отчет по отдельному объекту групповых политик (Defaul Domain Policy)
PS C:\>Get-GPOReport -name "Default Domain Policy" -ReportType HTML -Path "c:\work\ddp.htm"
invoke-item "c:\work\ddp.htm"
#Создаем отчеты по всем объектам групповых политик
PS C:\>Get-GPOReport -All -ReportType HTML -Path "c:\work\allgpo.htm"
invoke-item "c:\work\allgpo.htm"
#Создаем для каждого объекта групповых политик свой отчет
#заменяем пробелы на _ в имени GPO
PS C:\>Get-GPO -all | foreach {
$f="{0}.htm" -f ($_.Displayname).Replace(" ","_")
$htmfile=Join-Path -Path "C:\work" -ChildPath $f
Get-GPOReport -Name $_.Displayname -ReportType HTML -Path $htmfile
Get-Item $htmfile
}
Problem number 2. Too many GPOs that are not used.
Task: find empty GPOs
- Define Group Policy Objects Without Settings
- Looking for XML ExtensionData
PS C:\> Import-Module GroupPolicy
PS C:\> [xml]$r = Get-GPOReport -Name MyGPO
-ReportType XML
PS C:\> if ((-Not $r.gpo.user.extensiondata) -AND (-not $r.gpo.computer.extensiondata)) {
"GPO is empty"
}
Additional teams
#requires -version 2.0
#find empty gpos in the domain
Function Get-EmptyGPO {
Param (
[Parameter(Position=0,ValueFromPipeline=$True,
ValueFromPipelinebyPropertyName=$True)]
[string]$DisplayName
)
Begin {
#import the GroupPolicy Module
Import-Module GroupPolicy
}
Process {
#create an XML report
[xml]$report=Get-GPOReport -Name $displayname -ReportType XML
#totally empty
if ((-Not $report.gpo.user.extensiondata) -AND (-not $report.gpo.computer.extensiondata)) {
#no extension data so write
Get-GPO -Name $Displayname
}
} #process
End {}
} #function
Function Test-EmptyGPO {
Param (
[Parameter(Position=0,ValueFromPipeline=$True,
ValueFromPipelinebyPropertyName=$True)]
[string]$DisplayName
)
Begin {
#import the GroupPolicy Module
Import-Module GroupPolicy
}
Process {
#set default values
$User=$False
$Computer=$False
#create an XML report
[xml]$report=Get-GPOReport -Name $displayname -ReportType XML
if ($report.gpo.user.extensiondata) {
$User=$True
}
If ( $report.gpo.computer.extensiondata) {
$Computer=$True
}
#write a custom object to the pipeline
New-Object -TypeName PSObject -Property @{
Displayname=$report.gpo.name
UserData=$User
ComputerData=$Computer
}
} #Process
End {}
} #function
#Get-GPO -All | Get-EmptyGPO
#Get-GPO -All | Test-EmptyGPO
Problem number 3.
Who accessed the GPO?
Are there GPOs in which half of the policies are deactivated (“Are there any GPOs with 'half' the policy disabled?”)
Are there GPOs in which all policies are deactivated (“Are there any GPOs with 'all' the policy disabled?”)
We apply the filter according to the status of GPO (GPOStatus). Each of the three questions above corresponds to three teams:
PS C:\> get-gpo -all | Sort GPOStatus | format-table -GroupBy GPOStatus Displayname,*Time
PS C:\> get-gpo -all | where {$_.GPOStatus -match "disabled"} | Select GPOStatus,Displayname
PS C:\> get-gpo -all | where {$_.GPOStatus -match "AllSettingsDisabled"}
Analyzing Group Policy Objects
Problem number 4. Discovering GPOs Without Links
PS C:\> Import-Module ActiveDirectory
Get-ADOrganizationalUnit -filter * | select-object
-ExpandProperty DistinguishedName | get-adobject
-prop gplink | where {$_.gplink} | Select-object
-expand gplink | foreach-object {
foreach ($item in ($_.Split("]["))) {
$links+=$regex.match($item).Value
}
}
Get-GPO -All | Where {$links -notcontains $_.id}
Additional teams
#requires -version 2.0
<#
Find unlinked GPOs. This requires the Active Directory Module
This version does not query for site links
#>
Import-Module ActiveDirectory,GroupPolicy
#GUID regular expression pattern
[Regex]$RegEx = "(([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12})"
#create an array of distinguishednames
$dn=@()
$dn+=Get-ADDomain | select -ExpandProperty DistinguishedName
$dn+=Get-ADOrganizationalUnit -filter * | select -ExpandProperty DistinguishedName
$links=@()
#get domain and OU links
foreach ($container in $dn) {
#pull the GUID and add it to the array of links
get-adobject -identity $container -prop gplink |
where {$_.gplink} | Select -expand gplink | foreach {
#there might be multiple GPO links so split
foreach ($item in ($_.Split("]["))) {
$links+=$regex.match($item).Value
} #foreach item
} #foreach
} #foreach container
#$links
<#
get all gpos where the ID doesn't belong to the array
write the GPO to the pipeline
#>
Get-GPO -All | Where {$links -notcontains $_.id}
Problem 5. Group Policy objects with excessive settings in the registry (“Extra Registry Settings”)
Find excessive settings in the registry
#Use Xpath with the XML report data
PS C:\> [xml]$report = Get-GPOReport -Name MyGPO
-ReportType XML
PS C:\> $ns = @{q3 = "http://www.microsoft.com/GroupPolicy/Settings/Registry"}
PS C:\> $nodes = Select-Xml -Xml $report -Namespace $ns -XPath "//q3:RegistrySetting" | select -expand Node | Where {$_.AdmSetting -eq 'false'}
Additional teams
#requires -version 2.0
#find GPOs with extra registry, ie non-ADM settings
Function Test-GPOExtraRegistry {
Param (
[Parameter(Position=0,ValueFromPipeline=$True,
ValueFromPipelinebyPropertyName=$True)]
[string]$DisplayName
)
Begin {
#import the GroupPolicy Module
Import-Module GroupPolicy
}
Process {
#create an XML report
[xml]$report=Get-GPOReport -Name $displayname -ReportType XML
#define the XML namespace
$ns=@{q3="http://www.microsoft.com/GroupPolicy/Settings/Registry"}
$nodes=Select-Xml -Xml $report -Namespace $ns -XPath "//q3:RegistrySetting" |
select -expand Node | Where {$_.AdmSetting -eq 'false'}
if ($nodes) {
#extra settings were found so get the GPO and write it to the pipeline
Get-GPO -Name $Displayname
}
}
End {}
} #function
#Import-Module GroupPolicy
#Get-GPO -all | Test-GPOExtraRegistry
Once again, we indicate that in the post we presented only the dry residue of what was demonstrated in the report. The report itself can be viewed here .
Bonuses:
- Script Link
- Slides
- A free book on Group Policy + PowerShell from one of the authors of the report [ENG] (you must fill out the form)
You can also use our NetWrix Group Policy Change Reporter program to generate group policy reports .