Public/Enable-DCPerformanceBaseline.ps1

function Enable-DCPerformanceBaseline {
<#
.SYNOPSIS
    Deploys a performance baseline Data Collector Set across all domain controllers.
 
.DESCRIPTION
    Remotely creates and starts a logman Data Collector Set on every DC in the domain.
    Collects key performance counters including CPU, memory, disk, network, and NTDS/LDAP
    metrics at 5-minute intervals. Output is stored locally on each DC under
    C:\PerfLogs\DC_Baseline\ in a circular binary log capped at 500MB.
 
    Writes a small deployment script to each DC over SMB (C$) and launches it via
    WMI Win32_Process - no PSRemoting/WinRM required. The deployment script runs
    logman locally on the DC and records logman's real exit code and console output
    to a result file, which this function reads back over SMB so the reported
    success/failure/already-exists status reflects what logman actually did rather
    than a guess based on the WMI process-creation return code.
 
    Safe to re-run - reports "already exists"/"already running" from logman's own
    output rather than erroring out.
 
.EXAMPLE
    Enable-DCPerformanceBaseline
    Deploys the baseline collector to all DCs in the current domain.
 
.NOTES
    Author: K Shankar R Karanth
    Website: https://karanth.ovh
    Version: 2.0
    Run as Domain Admin or equivalent with WMI/DCOM and SMB (C$) access to all DCs.
    Read-only against AD; makes changes on each DC's local filesystem and starts a
    local performance-monitoring service (Data Collector Set) on each DC.
#>


    [CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param()

    Set-StrictMode -Version Latest
    $ResultWaitSeconds = 30
    $remoteLogFolder = 'C:\PerfLogs\DC_Baseline'
    $remoteScriptPath = "$remoteLogFolder\Deploy-Baseline.ps1"

    # This becomes a real .ps1 file on the target DC, so normal PowerShell line
    # continuation and quoting work as expected - no WMI command-line escaping needed.
    $deployScriptContent = @'
$ErrorActionPreference = 'Stop'
$logFolder = 'C:\PerfLogs\DC_Baseline'
$resultPath = Join-Path $logFolder 'Deploy-Baseline.result.json'
 
$result = [ordered]@{
    FolderReady = $false
    CollectorCreateExitCode = $null
    CollectorCreateOutput = $null
    CollectorStartExitCode = $null
    CollectorStartOutput = $null
    Error = $null
}
 
try {
    if (-not (Test-Path -LiteralPath $logFolder)) {
        New-Item -ItemType Directory -Path $logFolder -Force | Out-Null
    }
    $result.FolderReady = $true
 
    $createOutput = & logman.exe create counter DC_Baseline_Monitoring `
        -c "\Processor(_Total)\% Processor Time" `
           "\Memory\Available MBytes" `
           "\Memory\Pages/sec" `
           "\LogicalDisk(_Total)\% Free Space" `
           "\PhysicalDisk(_Total)\Disk Reads/sec" `
           "\PhysicalDisk(_Total)\Disk Writes/sec" `
           "\Network Interface(*)\Bytes Total/sec" `
           "\NTDS\LDAP Searches/sec" `
           "\NTDS\DS Directory Reads/sec" `
           "\NTDS\DS Directory Writes/sec" `
        -si 00:05:00 `
        -o "C:\PerfLogs\DC_Baseline\DC_Baseline_Monitoring" `
        -f bincirc `
        -max 500 `
        -v mmddhhmm `
        -cnf 01:00:00 2>&1 | Out-String
    $result.CollectorCreateExitCode = $LASTEXITCODE
    $result.CollectorCreateOutput = $createOutput.Trim()
 
    $startOutput = & logman.exe start DC_Baseline_Monitoring 2>&1 | Out-String
    $result.CollectorStartExitCode = $LASTEXITCODE
    $result.CollectorStartOutput = $startOutput.Trim()
}
catch {
    $result.Error = $_.Exception.Message
}
finally {
    ($result | ConvertTo-Json) | Set-Content -LiteralPath $resultPath -Encoding UTF8
}
'@


    $DomainControllers = Get-ADDomainController -Filter * | Select-Object -ExpandProperty HostName

    foreach ($DC in $DomainControllers) {
        Write-Host "`nConfiguring $DC..." -ForegroundColor Cyan

        if (-not $PSCmdlet.ShouldProcess($DC, 'Deploy and start performance baseline Data Collector Set')) {
            continue
        }

        try {
            $uncLogFolder   = "\\$DC\C$\PerfLogs\DC_Baseline"
            $uncScriptPath  = "\\$DC\C$\PerfLogs\DC_Baseline\Deploy-Baseline.ps1"
            $uncResultPath  = "\\$DC\C$\PerfLogs\DC_Baseline\Deploy-Baseline.result.json"

            if (Test-Path -LiteralPath $uncResultPath) {
                Remove-Item -LiteralPath $uncResultPath -Force -ErrorAction SilentlyContinue
            }
            if (-not (Test-Path -LiteralPath $uncLogFolder)) {
                New-Item -ItemType Directory -Path $uncLogFolder -Force | Out-Null
            }
            Set-Content -LiteralPath $uncScriptPath -Value $deployScriptContent -Encoding UTF8 -Force

            $wmi = [WMIClass]"\\$DC\root\cimv2:Win32_Process"
            $launch = $wmi.Create("powershell.exe -NonInteractive -ExecutionPolicy Bypass -File `"$remoteScriptPath`"")
            if ($launch.ReturnValue -ne 0) {
                Write-Warning " [WARN] Could not launch deployment process on $DC (WMI ReturnValue: $($launch.ReturnValue))"
                continue
            }

            $waited = 0
            $pollIntervalSeconds = 2
            while (-not (Test-Path -LiteralPath $uncResultPath) -and $waited -lt $ResultWaitSeconds) {
                Start-Sleep -Seconds $pollIntervalSeconds
                $waited += $pollIntervalSeconds
            }

            if (-not (Test-Path -LiteralPath $uncResultPath)) {
                Write-Warning " [WARN] Timed out after $ResultWaitSeconds second(s) waiting for deployment result from $DC"
                continue
            }

            $deployResult = Get-Content -LiteralPath $uncResultPath -Raw | ConvertFrom-Json

            if ($deployResult.Error) {
                Write-Warning " [WARN] Deployment script reported an error on $DC : $($deployResult.Error)"
                continue
            }

            if ($deployResult.CollectorCreateExitCode -eq 0) {
                Write-Host " [OK] Data Collector Set created on $DC"
            }
            else {
                Write-Warning " [WARN] logman create exited $($deployResult.CollectorCreateExitCode) on $DC : $($deployResult.CollectorCreateOutput)"
            }

            if ($deployResult.CollectorStartExitCode -eq 0) {
                Write-Host " [OK] Data Collector Set started on $DC"
            }
            else {
                Write-Warning " [WARN] logman start exited $($deployResult.CollectorStartExitCode) on $DC : $($deployResult.CollectorStartOutput)"
            }
        }
        catch {
            Write-Error "Failed to contact $DC : $_"
        }
    }

    Write-Host "`nDone. Review warnings above for any DCs that need attention." -ForegroundColor Cyan
}