Skip to content

Instantly share code, notes, and snippets.

@davidlu1001
Last active July 8, 2024 00:08
Show Gist options
  • Select an option

  • Save davidlu1001/e78cccf1bebf1bdee705b8cc4338cc8a to your computer and use it in GitHub Desktop.

Select an option

Save davidlu1001/e78cccf1bebf1bdee705b8cc4338cc8a to your computer and use it in GitHub Desktop.
Update Config With CSV (includes old and new value)
[CmdletBinding()]
param(
[Parameter(ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
[string[]]$ComputerNames = @($env:COMPUTERNAME),
[string[]]$ScanPaths = @("D:\", "E:\"),
[string]$OldNewDnsFile = "old_new_dns.csv",
[string]$OutputFile = "scanConfigResult_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv",
[string]$LogFile = "ScanLog_$(Get-Date -Format 'yyyyMMdd_HHmmss').log",
[int]$MaxConcurrentJobs = 5,
[int]$BatchSize = 1000,
[switch]$DryRun
)
$ErrorActionPreference = "SilentlyContinue"
function Write-Log {
param([string]$Message)
$logMessage = "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $Message"
Add-Content -Path $LogFile -Value $logMessage
Write-Verbose $logMessage
}
function Update-ConfigFiles {
param (
[string]$ComputerName,
[string[]]$Paths,
[array]$OldNewDnsEntries,
[int]$BatchSize,
[bool]$IsDryRun
)
$scriptBlock = {
param($Paths, $OldNewDnsEntries, $BatchSize, $IsDryRun)
function Process-FileBatch {
param($Files)
$results = @()
foreach ($file in $Files) {
try {
Write-Verbose "Processing file: $($file.FullName)"
$content = [System.IO.File]::ReadAllText($file.FullName)
$fileResults = @()
foreach ($entry in $OldNewDnsEntries) {
$matchCount = ([regex]::Matches($content, [regex]::Escape($entry.oldValue), [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)).Count
if ($matchCount -gt 0) {
if ($IsDryRun) {
Write-Host "DryRun: In $($file.FullName), $($entry.oldValue) will be replaced with $($entry.newValue), with matchCount $matchCount"
} else {
$content = [regex]::Replace($content, [regex]::Escape($entry.oldValue), $entry.newValue, [System.Text.RegularExpressions.RegexOptions]::IgnoreCase)
Write-Host "Replaced $($entry.oldValue) with $($entry.newValue) in $($file.FullName) ($matchCount occurrences)"
}
$fileResults += [PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
ConfigPath = $file.FullName
OldValue = $entry.oldValue
NewValue = $entry.newValue
MatchCount = $matchCount
Result = if ($IsDryRun) { "WillReplace" } else { "Replaced" }
}
}
}
if ($fileResults.Count -eq 0) {
$fileResults += [PSCustomObject]@{
ComputerName = $env:COMPUTERNAME
ConfigPath = $file.FullName
OldValue = "NA"
NewValue = "NA"
MatchCount = 0
Result = "NotFound"
}
}
$results += $fileResults
if (-not $IsDryRun -and $fileResults.Where({$_.Result -eq "Replaced"}).Count -gt 0) {
[System.IO.File]::WriteAllText($file.FullName, $content)
}
}
catch {
Write-Warning "Error processing file $($file.FullName): $_"
}
}
return $results
}
$allFiles = @()
foreach ($path in $Paths) {
try {
Write-Verbose "Scanning path: $path"
$allFiles += Get-ChildItem -Path $path -Recurse -Include *.config, *.json -ErrorAction Stop
}
catch {
Write-Warning "Error accessing path $path: $_"
}
}
$totalFiles = $allFiles.Count
Write-Verbose "Total files found: $totalFiles"
$results = @()
for ($i = 0; $i -lt $totalFiles; $i += $BatchSize) {
$batch = $allFiles | Select-Object -Skip $i -First $BatchSize
Write-Verbose "Processing batch $($i / $BatchSize + 1) of $([math]::Ceiling($totalFiles / $BatchSize))"
$batchResults = Process-FileBatch -Files $batch
$results += $batchResults
Write-Verbose "Processed $($i + $batch.Count) of $totalFiles files"
}
return $results
}
try {
Write-Verbose "Executing on computer: $ComputerName"
if ($ComputerName -eq $env:COMPUTERNAME) {
return & $scriptBlock $Paths $OldNewDnsEntries $BatchSize $IsDryRun
} else {
$session = New-PSSession -ComputerName $ComputerName -ErrorAction Stop
return Invoke-Command -Session $session -ScriptBlock $scriptBlock -ArgumentList $Paths, $OldNewDnsEntries, $BatchSize, $IsDryRun
}
}
catch {
Write-Log "Error updating config files on $ComputerName: $_"
return $null
}
finally {
if ($session) {
Remove-PSSession $session
}
}
}
try {
Write-Log "Starting update process on computers: $($ComputerNames -join ', ')"
Write-Verbose "DryRun mode: $DryRun"
# Read old_new_dns.csv
Write-Verbose "Reading old_new_dns.csv file"
$oldNewDnsEntries = Import-Csv -Path $OldNewDnsFile
Write-Verbose "Found $($oldNewDnsEntries.Count) entries in old_new_dns.csv"
$jobs = @()
foreach ($computer in $ComputerNames) {
Write-Log "Initiating update on $computer"
$job = Start-Job -ScriptBlock ${function:Update-ConfigFiles} -ArgumentList $computer, $ScanPaths, $oldNewDnsEntries, $BatchSize, $DryRun
$jobs += $job
while (($jobs | Where-Object { $_.State -eq 'Running' }).Count -ge $MaxConcurrentJobs) {
Write-Verbose "Waiting for a job to complete. Current running jobs: $(($jobs | Where-Object { $_.State -eq 'Running' }).Count)"
$completedJob = $jobs | Wait-Job -Any
$results = Receive-Job $completedJob
if ($results) {
$results | Export-Csv -Path $OutputFile -NoTypeInformation -Append
}
Remove-Job $completedJob
$jobs = @($jobs | Where-Object { $_.State -ne 'Completed' })
}
}
while ($jobs) {
Write-Verbose "Waiting for remaining jobs to complete. Jobs left: $($jobs.Count)"
$completedJob = $jobs | Wait-Job -Any
$results = Receive-Job $completedJob
if ($results) {
$results | Export-Csv -Path $OutputFile -NoTypeInformation -Append
}
Remove-Job $completedJob
$jobs = @($jobs | Where-Object { $_.State -ne 'Completed' })
}
Write-Log "Update process complete. Results saved to $OutputFile"
# stats
Write-Verbose "Calculating final statistics"
$results = Import-Csv -Path $OutputFile
$hostCount = @($results | Select-Object -Unique ComputerName).Count
$replacedCount = @($results | Where-Object { $_.Result -eq "Replaced" -or $_.Result -eq "WillReplace" }).Count
$notFoundCount = @($results | Where-Object { $_.Result -eq "NotFound" }).Count
$totalMatchCount = ($results | Measure-Object -Property MatchCount -Sum).Sum
Write-Log "Processed $hostCount hosts, replaced/will replace $replacedCount entries, $notFoundCount entries not found, total matches: $totalMatchCount"
Write-Verbose "Hosts processed: $hostCount"
Write-Verbose "Entries replaced/will be replaced: $replacedCount"
Write-Verbose "Entries not found: $notFoundCount"
Write-Verbose "Total matches: $totalMatchCount"
}
catch {
Write-Log "Critical error in main script execution: $_"
}
finally {
Write-Log "Script execution completed"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment