CIS Benchmarks with Powershell Scripts
POWERSHELL - multifile input
#Requires -Version 5.1
<#
.SYNOPSIS
Multi-File Registry SID Processor - PowerShell Version
.DESCRIPTION
This PowerShell script processes multiple Windows Registry files by replacing
<***USER_SID***> placeholders with the current user's actual SID and then
executing the registry changes. Supports single files, multiple files, file
lists, and configurable source locations including network paths.
.PARAMETER Files
Array of registry filenames to process
.PARAMETER FileList
Path to a text file containing a list of registry files to process
.PARAMETER ConfigFile
Path to configuration file (JSON format)
.PARAMETER SourcePath
Override the source path for registry files
.PARAMETER Preview
Show preview of changes without applying them
.PARAMETER Force
Skip confirmation prompts
.PARAMETER Verbose
Enable verbose output
.EXAMPLE
.\Process-RegistryMulti.ps1 -Files "screensaver_policy.reg"
Process a single registry file
.EXAMPLE
.\Process-RegistryMulti.ps1 -Files "policy1.reg","policy2.reg","policy3.reg"
Process multiple registry files
.EXAMPLE
.\Process-RegistryMulti.ps1 -FileList "company_policies.txt"
Process files listed in a text file
.EXAMPLE
.\Process-RegistryMulti.ps1 -Files "policy.reg" -SourcePath "\\server\share\registry\"
Process file from network location
.EXAMPLE
.\Process-RegistryMulti.ps1 -FileList "policies.txt" -Preview
Preview changes without applying them
.NOTES
Version: 2.0
Author: Registry SID Processor
Requires: PowerShell 5.1 or later, Administrator privileges for some registry keys
#>
[CmdletBinding(DefaultParameterSetName = 'Files')]
param(
[Parameter(ParameterSetName = 'Files', Position = 0)]
[string[]]$Files,
[Parameter(ParameterSetName = 'FileList')]
[string]$FileList,
[Parameter()]
[string]$ConfigFile = "registry_config.json",
[Parameter()]
[string]$SourcePath,
[Parameter()]
[switch]$Preview,
[Parameter()]
[switch]$Force,
[Parameter()]
[switch]$WhatIf
)
# Script configuration
$script:Config = @{
SourcePath = ".\registry_files\"
TempDir = $null
LogFile = $null
BackupEnabled = $true
BackupPath = ".\registry_backups\"
}
$script:Stats = @{
ProcessedCount = 0
FailedCount = 0
SkippedCount = 0
StartTime = Get-Date
}
# Initialize logging
function Initialize-Logging {
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$script:Config.LogFile = ".\registry_processor_$timestamp.log"
Write-Log "Registry SID Processor v2.0 - PowerShell Edition" -Level "INFO"
Write-Log "Started at: $(Get-Date)" -Level "INFO"
Write-Log "User: $env:USERNAME" -Level "INFO"
Write-Log "Computer: $env:COMPUTERNAME" -Level "INFO"
}
# Logging function
function Write-Log {
param(
[string]$Message,
[ValidateSet("INFO", "WARN", "ERROR", "DEBUG")]
[string]$Level = "INFO"
)
$timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$logEntry = "[$timestamp] [$Level] $Message"
# Console output with colors
switch ($Level) {
"ERROR" { Write-Host $logEntry -ForegroundColor Red }
"WARN" { Write-Host $logEntry -ForegroundColor Yellow }
"DEBUG" { if ($VerbosePreference -eq "Continue") { Write-Host $logEntry -ForegroundColor Gray } }
default { Write-Host $logEntry -ForegroundColor White }
}
# File output
if ($script:Config.LogFile) {
Add-Content -Path $script:Config.LogFile -Value $logEntry -ErrorAction SilentlyContinue
}
}
# Load configuration from JSON file
function Load-Configuration {
param([string]$ConfigPath)
if (Test-Path $ConfigPath) {
try {
Write-Log "Loading configuration from: $ConfigPath" -Level "INFO"
$configData = Get-Content $ConfigPath -Raw | ConvertFrom-Json
if ($configData.SourcePath) { $script:Config.SourcePath = $configData.SourcePath }
if ($configData.TempDir) { $script:Config.TempDir = $configData.TempDir }
if ($configData.BackupEnabled -ne $null) { $script:Config.BackupEnabled = $configData.BackupEnabled }
if ($configData.BackupPath) { $script:Config.BackupPath = $configData.BackupPath }
Write-Log "Configuration loaded successfully" -Level "INFO"
}
catch {
Write-Log "Failed to load configuration: $($_.Exception.Message)" -Level "WARN"
Write-Log "Using default configuration" -Level "INFO"
}
}
else {
Write-Log "Configuration file not found: $ConfigPath" -Level "WARN"
Write-Log "Using default configuration" -Level "INFO"
}
}
# Get current user's SID
function Get-CurrentUserSID {
try {
Write-Log "Detecting current user SID..." -Level "INFO"
# Method 1: Using .NET Security Principal
$user = [System.Security.Principal.WindowsIdentity]::GetCurrent()
if ($user -and $user.User) {
$sid = $user.User.Value
Write-Log "SID detected using .NET method: $sid" -Level "DEBUG"
return $sid
}
# Method 2: Using WMI
$userAccount = Get-WmiObject -Class Win32_UserAccount -Filter "Name='$env:USERNAME'"
if ($userAccount) {
$sid = $userAccount.SID
Write-Log "SID detected using WMI method: $sid" -Level "DEBUG"
return $sid
}
# Method 3: Using whoami command
$whoamiOutput = & whoami /user /fo csv | ConvertFrom-Csv
if ($whoamiOutput -and $whoamiOutput.SID) {
$sid = $whoamiOutput.SID
Write-Log "SID detected using whoami method: $sid" -Level "DEBUG"
return $sid
}
throw "All SID detection methods failed"
}
catch {
Write-Log "Failed to detect user SID: $($_.Exception.Message)" -Level "ERROR"
return $null
}
}
# Create backup of registry key
function Backup-RegistryKey {
param(
[string]$KeyPath,
[string]$BackupName
)
if (-not $script:Config.BackupEnabled) {
return $true
}
try {
$backupDir = $script:Config.BackupPath
if (-not (Test-Path $backupDir)) {
New-Item -Path $backupDir -ItemType Directory -Force | Out-Null
}
$timestamp = Get-Date -Format "yyyyMMdd_HHmmss"
$backupFile = Join-Path $backupDir "$BackupName`_$timestamp.reg"
Write-Log "Creating backup: $backupFile" -Level "DEBUG"
# Use reg export command
$result = & reg export $KeyPath $backupFile /y 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Log "Backup created successfully: $backupFile" -Level "INFO"
return $true
}
else {
Write-Log "Backup failed: $result" -Level "WARN"
return $false
}
}
catch {
Write-Log "Backup error: $($_.Exception.Message)" -Level "WARN"
return $false
}
}
# Process a single registry file
function Process-RegistryFile {
param(
[string]$FileName,
[string]$UserSID,
[switch]$PreviewOnly
)
$sourceFile = Join-Path $script:Config.SourcePath $FileName
Write-Log "Processing file: $FileName" -Level "INFO"
Write-Log "Source path: $sourceFile" -Level "DEBUG"
# Check if source file exists
if (-not (Test-Path $sourceFile)) {
Write-Log "File not found: $sourceFile" -Level "ERROR"
$script:Stats.FailedCount++
return $false
}
try {
# Read and process file content
$content = Get-Content $sourceFile -Raw
$processedContent = $content -replace '<\*\*\*USER_SID\*\*\*>', $UserSID
# Create temporary file
$tempDir = if ($script:Config.TempDir) { $script:Config.TempDir } else { $env:TEMP }
$tempFile = Join-Path $tempDir "processed_$(Get-Random)_$FileName"
# Ensure temp directory exists
$tempFileDir = Split-Path $tempFile -Parent
if (-not (Test-Path $tempFileDir)) {
New-Item -Path $tempFileDir -ItemType Directory -Force | Out-Null
}
# Write processed content
Set-Content -Path $tempFile -Value $processedContent -Encoding UTF8
Write-Log "Processed file created: $tempFile" -Level "DEBUG"
if ($PreviewOnly) {
Write-Log "PREVIEW MODE - Registry content:" -Level "INFO"
Write-Host "=" * 50 -ForegroundColor Cyan
Write-Host $processedContent -ForegroundColor Gray
Write-Host "=" * 50 -ForegroundColor Cyan
$script:Stats.ProcessedCount++
return $true
}
# Extract registry keys for backup
$registryKeys = $processedContent | Select-String -Pattern '\[HKEY_[^\]]+\]' -AllMatches |
ForEach-Object { $_.Matches.Value -replace '^\[|\]$', '' }
# Create backups
foreach ($key in $registryKeys) {
$backupName = "$FileName`_$(($key -split '\\')[-1])"
Backup-RegistryKey -KeyPath $key -BackupName $backupName
}
# Apply registry changes
Write-Log "Applying registry changes..." -Level "INFO"
if ($WhatIf) {
Write-Log "WHATIF: Would apply registry file: $tempFile" -Level "INFO"
$script:Stats.ProcessedCount++
return $true
}
$result = & reg import $tempFile 2>&1
if ($LASTEXITCODE -eq 0) {
Write-Log "Registry changes applied successfully for: $FileName" -Level "INFO"
$script:Stats.ProcessedCount++
$success = $true
}
else {
Write-Log "Failed to apply registry changes for: $FileName" -Level "ERROR"
Write-Log "Error output: $result" -Level "ERROR"
$script:Stats.FailedCount++
$success = $false
}
# Cleanup temporary file
Remove-Item $tempFile -Force -ErrorAction SilentlyContinue
return $success
}
catch {
Write-Log "Error processing file $FileName`: $($_.Exception.Message)" -Level "ERROR"
$script:Stats.FailedCount++
return $false
}
}
# Load file list from text file
function Get-FileListFromFile {
param([string]$ListFile)
if (-not (Test-Path $ListFile)) {
Write-Log "File list not found: $ListFile" -Level "ERROR"
return @()
}
try {
$files = Get-Content $ListFile |
Where-Object { $_ -and $_ -notmatch '^\s*#' -and $_.Trim() -ne '' } |
ForEach-Object { $_.Trim() }
Write-Log "Loaded $($files.Count) files from list: $ListFile" -Level "INFO"
return $files
}
catch {
Write-Log "Error reading file list: $($_.Exception.Message)" -Level "ERROR"
return @()
}
}
# Show processing summary
function Show-Summary {
$endTime = Get-Date
$duration = $endTime - $script:Stats.StartTime
Write-Host "`n" + "=" * 60 -ForegroundColor Cyan
Write-Host "PROCESSING SUMMARY" -ForegroundColor Cyan
Write-Host "=" * 60 -ForegroundColor Cyan
Write-Host "Files processed successfully: " -NoNewline
Write-Host $script:Stats.ProcessedCount -ForegroundColor Green
Write-Host "Files failed: " -NoNewline
Write-Host $script:Stats.FailedCount -ForegroundColor Red
Write-Host "Files skipped: " -NoNewline
Write-Host $script:Stats.SkippedCount -ForegroundColor Yellow
$total = $script:Stats.ProcessedCount + $script:Stats.FailedCount + $script:Stats.SkippedCount
Write-Host "Total files: " -NoNewline
Write-Host $total -ForegroundColor White
Write-Host "Duration: " -NoNewline
Write-Host $duration.ToString("hh\:mm\:ss") -ForegroundColor White
Write-Host "Log file: " -NoNewline
Write-Host $script:Config.LogFile -ForegroundColor White
Write-Host "=" * 60 -ForegroundColor Cyan
Write-Log "Processing completed. Summary: Processed=$($script:Stats.ProcessedCount), Failed=$($script:Stats.FailedCount), Skipped=$($script:Stats.SkippedCount)" -Level "INFO"
}
# Main execution function
function Main {
try {
# Initialize
Initialize-Logging
# Load configuration
Load-Configuration -ConfigPath $ConfigFile
# Override source path if specified
if ($SourcePath) {
$script:Config.SourcePath = $SourcePath
Write-Log "Source path overridden: $SourcePath" -Level "INFO"
}
Write-Log "Source path: $($script:Config.SourcePath)" -Level "INFO"
# Get user SID
$userSID = Get-CurrentUserSID
if (-not $userSID) {
Write-Log "Cannot proceed without user SID" -Level "ERROR"
return 1
}
Write-Log "Current user SID: $userSID" -Level "INFO"
# Determine files to process
$filesToProcess = @()
if ($FileList) {
$filesToProcess = Get-FileListFromFile -ListFile $FileList
}
elseif ($Files) {
$filesToProcess = $Files
}
else {
Write-Log "No files specified. Use -Files or -FileList parameter." -Level "ERROR"
return 1
}
if ($filesToProcess.Count -eq 0) {
Write-Log "No files to process" -Level "WARN"
return 0
}
Write-Log "Files to process: $($filesToProcess.Count)" -Level "INFO"
# Confirmation prompt
if (-not $Force -and -not $Preview -and -not $WhatIf) {
Write-Host "`nFiles to be processed:" -ForegroundColor Yellow
$filesToProcess | ForEach-Object { Write-Host " - $_" -ForegroundColor Gray }
Write-Host "`nSource: $($script:Config.SourcePath)" -ForegroundColor Yellow
$response = Read-Host "`nDo you want to proceed? (Y/N)"
if ($response -notmatch '^[Yy]') {
Write-Log "Operation cancelled by user" -Level "INFO"
return 0
}
}
# Process files
foreach ($file in $filesToProcess) {
$success = Process-RegistryFile -FileName $file -UserSID $userSID -PreviewOnly:$Preview
if (-not $success) {
Write-Log "Failed to process: $file" -Level "ERROR"
}
}
# Show summary
Show-Summary
# Return appropriate exit code
return if ($script:Stats.FailedCount -gt 0) { 1 } else { 0 }
}
catch {
Write-Log "Unexpected error: $($_.Exception.Message)" -Level "ERROR"
Write-Log "Stack trace: $($_.ScriptStackTrace)" -Level "DEBUG"
return 1
}
}
# Script entry point
if ($MyInvocation.InvocationName -ne '.') {
$exitCode = Main
exit $exitCode
}
USAGE_EXAMPLES.ps1
# PowerShell Registry Processor - Usage Examples
# ==============================================
# This file contains various usage examples for the Process-RegistryMulti.ps1 script
# Example 1: Process a single registry file
# .\Process-RegistryMulti.ps1 -Files "screensaver_policy.reg"
# Example 2: Process multiple registry files
# .\Process-RegistryMulti.ps1 -Files "screensaver_policy.reg","windows_update_policy.reg","security_policy.reg"
# Example 3: Process files from a list
# .\Process-RegistryMulti.ps1 -FileList "company_policies.txt"
# Example 4: Preview changes without applying them
# .\Process-RegistryMulti.ps1 -Files "screensaver_policy.reg" -Preview
# Example 5: Use custom configuration file
# .\Process-RegistryMulti.ps1 -ConfigFile "production_config.json" -FileList "policies.txt"
# Example 6: Override source path
# .\Process-RegistryMulti.ps1 -Files "policy.reg" -SourcePath "\\server\share\registry\"
# Example 7: Force execution without prompts
# .\Process-RegistryMulti.ps1 -Files "policy.reg" -Force
# Example 8: What-if mode (show what would be done)
# .\Process-RegistryMulti.ps1 -Files "policy.reg" -WhatIf
# Example 9: Verbose output for debugging
# .\Process-RegistryMulti.ps1 -Files "policy.reg" -Verbose
# Example 10: Combine multiple options
# .\Process-RegistryMulti.ps1 -FileList "policies.txt" -SourcePath "\\server\registry\" -Force -Verbose
# Advanced Examples
# =================
# Process files with error handling
try {
.\Process-RegistryMulti.ps1 -Files "critical_policy.reg" -Force
Write-Host "Registry processing completed successfully" -ForegroundColor Green
}
catch {
Write-Host "Registry processing failed: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
# Process files and capture output
$result = .\Process-RegistryMulti.ps1 -Files "policy.reg" -Force
if ($LASTEXITCODE -eq 0) {
Write-Host "Success: All registry changes applied" -ForegroundColor Green
} else {
Write-Host "Error: Some registry changes failed" -ForegroundColor Red
}
# Batch processing with different configurations
$environments = @("development", "testing", "production")
foreach ($env in $environments) {
Write-Host "Processing $env environment..." -ForegroundColor Yellow
.\Process-RegistryMulti.ps1 -ConfigFile "$env`_config.json" -FileList "$env`_policies.txt" -Force
}
# Network path processing with retry logic
$maxRetries = 3
$retryCount = 0
do {
try {
.\Process-RegistryMulti.ps1 -SourcePath "\\server\registry\" -FileList "policies.txt" -Force
break
}
catch {
$retryCount++
Write-Host "Attempt $retryCount failed, retrying..." -ForegroundColor Yellow
Start-Sleep -Seconds 5
}
} while ($retryCount -lt $maxRetries)
# Scheduled task integration
# Register-ScheduledTask -TaskName "RegistryUpdate" -Action (New-ScheduledTaskAction -Execute "PowerShell.exe" -Argument "-File C:\Scripts\Process-RegistryMulti.ps1 -FileList C:\Policies\daily_updates.txt -Force") -Trigger (New-ScheduledTaskTrigger -Daily -At "02:00")
# Group Policy integration example
# if (Test-Path "\\domain\sysvol\policies\registry\") {
# .\Process-RegistryMulti.ps1 -SourcePath "\\domain\sysvol\policies\registry\" -FileList "user_policies.txt" -Force
# }
PRODUCTION_CONFIG.JSON
{
"SourcePath": "\\\\policyserver\\registry\\production\\",
"TempDir": "C:\\Temp\\RegistryProcessor\\",
"BackupEnabled": true,
"BackupPath": "C:\\RegistryBackups\\Production\\",
"LogLevel": "INFO",
"MaxConcurrentFiles": 3,
"NetworkTimeout": 60,
"RetryAttempts": 5,
"Comments": {
"Description": "Production environment configuration",
"SourcePath": "Network path to production registry policies",
"TempDir": "Dedicated temp directory for production processing",
"BackupPath": "Production backup location with proper permissions",
"NetworkTimeout": "Increased timeout for network operations",
"RetryAttempts": "More retry attempts for critical production environment"
}
}
REGISTRY_CONFIG.JSON
{
"SourcePath": ".\\registry_files\\",
"TempDir": null,
"BackupEnabled": true,
"BackupPath": ".\\registry_backups\\",
"LogLevel": "INFO",
"MaxConcurrentFiles": 5,
"NetworkTimeout": 30,
"RetryAttempts": 3,
"Comments": {
"SourcePath": "Local folder or network path (e.g., '\\\\server\\share\\registry\\')",
"TempDir": "Custom temp directory (null = use system temp)",
"BackupEnabled": "Create registry backups before applying changes",
"BackupPath": "Directory for registry backups",
"LogLevel": "Logging level: DEBUG, INFO, WARN, ERROR",
"MaxConcurrentFiles": "Maximum files to process concurrently",
"NetworkTimeout": "Network timeout in seconds",
"RetryAttempts": "Number of retry attempts for failed operations"
}
}