# AgentMeshOS Windows read-only telemetry deployment. [CmdletBinding()] param( [ValidateSet('install', 'repair', 'check', 'uninstall')] [string]$Action = 'check', [string]$BindAddress = '100.64.0.7', [int]$Port = 9182, [string]$Proxy = 'socks5h://127.0.0.1:4000' ) $ErrorActionPreference = 'Stop' $ScriptName = 'agentmeshos-windows-metrics.ps1' $ScriptProject = 'AgentMeshOS' $ScriptVersion = 'v0.1.1' $ScriptUpdated = '2026-09-21' $Version = '0.30.7' $MsiUrl = "https://github.com/prometheus-community/windows_exporter/releases/download/v$Version/windows_exporter-$Version-amd64.msi" $ExpectedSha256 = 'b7adcf630ff8a035921ffc100138c4aa0d8632f789e7a16f91e2912a96784bad' $MsiPath = Join-Path $env:TEMP "windows_exporter-$Version-amd64.msi" $ServiceName = 'windows_exporter' $FirewallRule = 'AgentMeshOS Windows telemetry (Tailnet only)' $Collectors = 'cpu,cs,logical_disk,memory,net,os,system' function Set-Firewall { Remove-NetFirewallRule -DisplayName $FirewallRule -ErrorAction SilentlyContinue New-NetFirewallRule -DisplayName $FirewallRule -Direction Inbound -Action Allow -Protocol TCP -LocalPort $Port -RemoteAddress '100.64.0.1' -Profile Any | Out-Null } function Set-ExporterServiceConfiguration { $service = Get-Service -Name $ServiceName -ErrorAction Stop if (-not (Get-Service -Name 'Tailscale' -ErrorAction SilentlyContinue)) { throw 'Tailscale service is required before windows_exporter configuration' } $dependencies = @($service.ServicesDependedOn | ForEach-Object { $_.Name }) if ($dependencies -notcontains 'Tailscale') { $dependencies += 'Tailscale' } & sc.exe config $ServiceName 'start=' 'delayed-auto' | Out-Null if ($LASTEXITCODE -ne 0) { throw "windows_exporter delayed start configuration failed: $LASTEXITCODE" } & sc.exe config $ServiceName 'depend=' ($dependencies -join '/') | Out-Null if ($LASTEXITCODE -ne 0) { throw "windows_exporter dependency configuration failed: $LASTEXITCODE" } & sc.exe failure $ServiceName 'reset=' '86400' 'actions=' 'restart/5000/restart/15000/restart/60000' | Out-Null if ($LASTEXITCODE -ne 0) { throw "windows_exporter recovery configuration failed: $LASTEXITCODE" } } function Restart-Exporter { $service = Get-Service -Name $ServiceName -ErrorAction Stop if ($service.Status -eq 'Running') { Restart-Service -Name $ServiceName -Force } else { Start-Service -Name $ServiceName } } function Install-Exporter { if (-not (Test-Path $MsiPath)) { & curl.exe --proxy $Proxy --fail --retry 3 -L -o $MsiPath $MsiUrl if ($LASTEXITCODE -ne 0) { throw "windows_exporter download failed: curl exit $LASTEXITCODE" } } $hash = (Get-FileHash $MsiPath -Algorithm SHA256).Hash.ToLowerInvariant() if ($hash -ne $ExpectedSha256) { throw "windows_exporter MSI SHA-256 mismatch: $hash" } $args = @("/i", $MsiPath, '/qn', '/norestart', "LISTEN_ADDR=$BindAddress", "LISTEN_PORT=$Port", "ENABLED_COLLECTORS=$Collectors") $process = Start-Process msiexec.exe -ArgumentList $args -Wait -PassThru if ($process.ExitCode -notin @(0, 3010)) { throw "windows_exporter MSI install failed: $($process.ExitCode)" } Set-Firewall Set-ExporterServiceConfiguration Restart-Exporter } function Repair-Exporter { Set-Firewall Set-ExporterServiceConfiguration Restart-Exporter } function Check-Exporter { $service = Get-Service $ServiceName -ErrorAction SilentlyContinue $listener = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue [pscustomobject]@{ script_version = $ScriptVersion service = if ($service) { [string]$service.Status } else { 'Missing' } listener = if ($listener) { ($listener | ForEach-Object { "$($_.LocalAddress):$($_.LocalPort)" }) -join ',' } else { 'Missing' } bind_address = $BindAddress port = $Port dependencies = if ($service) { (@($service.ServicesDependedOn | ForEach-Object { $_.Name }) -join ',') } else { '' } firewall = [bool](Get-NetFirewallRule -DisplayName $FirewallRule -ErrorAction SilentlyContinue) } | ConvertTo-Json -Compress } function Uninstall-Exporter { Remove-NetFirewallRule -DisplayName $FirewallRule -ErrorAction SilentlyContinue $product = Get-CimInstance Win32_Product -Filter "Name LIKE 'windows_exporter%'" -ErrorAction SilentlyContinue if ($product) { $product | ForEach-Object { Invoke-CimMethod -InputObject $_ -MethodName Uninstall | Out-Null } } } if ($Action -eq 'install') { Install-Exporter } elseif ($Action -eq 'repair') { Repair-Exporter } elseif ($Action -eq 'uninstall') { Uninstall-Exporter } else { Check-Exporter }