Notice: Due to size constraints and loading performance considerations, scripts referenced in blog posts are not attached directly. To request access, please complete the following form: Script Request Form Note: A Google account is required to access the form.
Disclaimer: I do not accept responsibility for any issues arising from scripts being run without adequate understanding. It is the user's responsibility to review and assess any code before execution. More information

Track Your Active Directory's Silent Changes: Exporting GPO Objects and Modified Time to CSV


Group Policy Objects are the backbone of Active Directory management, but they're also prime targets for changing centralized settings that can affect the whole domain.

I've created a PowerShell script that exports all domain GPOs to CSV format, perfect for baseline creation and integration with Microsoft Sentinel watchlists.

The Challenge

When you're managing an Active Directory environment, GPO changes can happen silently. New policy objects might be created by legitimate administrators or potentially by attackers who've gained elevated privileges. Having a baseline of existing GPOs becomes crucial for detecting unauthorized changes.

The Solution

This script captures all GPOs in your domain with key identifying information that can be imported directly into Microsoft Sentinel as a watchlist. Once imported, you can create analytics rules to alert on newly created GPOs that don't exist in your baseline.

# PowerShell script to export all Group Policy Objects to CSV
# Requires Group Policy module and appropriate domain permissions

# Import the Group Policy module
Import-Module GroupPolicy -ErrorAction Stop

# Set the output file path
$OutputPath = ".\GPO_Export_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"

# Get current timestamp for TimeGenerated field
$TimeGenerated = Get-Date -Format "yyyy-MM-dd HH:mm:ss"

try {
    Write-Host "Retrieving Group Policy Objects from domain..." -ForegroundColor Yellow
    
    # Get all GPOs in the domain
    $GPOs = Get-GPO -All
    
    # Create array to hold the results
    $Results = @()
    
    # Process each GPO
    foreach ($GPO in $GPOs) {
        $GPOObject = [PSCustomObject]@{
            TimeGenerated = $TimeGenerated
            GPOId = $GPO.Id
            GPOName = $GPO.DisplayName
            LastModified = $GPO.ModificationTime.ToString("yyyy-MM-dd HH:mm:ss")
        }
        $Results += $GPOObject
    }
    
    # Export to CSV
    $Results | Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8
    
    Write-Host "Successfully exported $($Results.Count) GPOs to: $OutputPath" -ForegroundColor Green
    Write-Host "File location: $(Resolve-Path $OutputPath)" -ForegroundColor Cyan
    
} catch {
    Write-Error "An error occurred: $($_.Exception.Message)"
    Write-Host "Please ensure you have:" -ForegroundColor Red
    Write-Host "1. Group Policy Management Console installed" -ForegroundColor Red
    Write-Host "2. Appropriate domain permissions" -ForegroundColor Red
    Write-Host "3. Connection to domain controller" -ForegroundColor Red
}

What does it do?

The script exports four key fields for each GPO:

  • TimeGenerated: When I ran the export (useful for baseline tracking)
  • GPOId: The unique GUID identifier for each policy
  • GPOName: The human-readable display name
  • LastModified: When the GPO was last changed

Sentinel Integration

Once you have the CSV file, you can import it directly into Microsoft Sentinel as a watchlist. This creates a baseline of known GPOs that I can reference in KQL queries. When Sentinel detects GPO creation events and cross-reference against this watchlist to identify newly created policies that weren't in the original baseline.

The output file gets a timestamp in the filename, so multiple runs won't overwrite previous exports. This makes it easy to track changes over time or create multiple baselines for different purposes.

Previous Post Next Post

نموذج الاتصال