[CmdletBinding()] param( [string]$Version = "1.38.4", [string]$ExpectedSha256 = "6c7878ec319471ac85b82443baec2f3fa5dbcf1b6e2da5d5cd2cbb60fff2bb45", [string]$ConfigurationTemplate = "$PSScriptRoot\mission-core-windows.conf.tmpl", [string]$PipelineCollector = "$PSScriptRoot\Get-NdcMissionCorePipelineTelemetry.ps1", [string]$PipelineJournal = "D:\NDC_MISSIONCORE\runtime\derived\.perception-persistent-publish\pipeline-telemetry.jsonl" ) $ErrorActionPreference = "Stop" $serviceName = "telegraf" $installRoot = "C:\Program Files\NDC\Mission Core\Telegraf" $configurationRoot = "C:\ProgramData\NDC\MissionCore\telemetry-agent" $configurationPath = Join-Path $configurationRoot "telegraf.conf" $collectorPath = Join-Path $configurationRoot "Get-NdcMissionCorePipelineTelemetry.ps1" $archiveUrl = "https://dl.influxdata.com/telegraf/releases/telegraf-$($Version)_windows_amd64.zip" $payload = [Console]::In.ReadToEnd() | ConvertFrom-Json foreach ($name in @( "MISSIONCORE_CONTOUR_ID", "MISSIONCORE_AGENT_ID", "MISSIONCORE_NODE_ID", "MISSIONCORE_MQTT_HOST", "MISSIONCORE_MQTT_PORT", "MISSIONCORE_MQTT_USERNAME", "MISSIONCORE_MQTT_PASSWORD", "MISSIONCORE_TELEMETRY_INTERVAL" )) { $value = $payload.$name if (-not $value) { throw "Provisioning payload is missing $name" } Set-Item -Path "Env:$name" -Value ([string]$value) } if (Get-Service -Name $serviceName -ErrorAction SilentlyContinue) { throw "Service '$serviceName' already exists; refusing an implicit replacement" } if (-not (Test-Path -LiteralPath $ConfigurationTemplate -PathType Leaf)) { throw "Configuration template not found: $ConfigurationTemplate" } if (-not (Test-Path -LiteralPath $PipelineCollector -PathType Leaf)) { throw "Pipeline collector not found: $PipelineCollector" } $journalParent = Get-Item -LiteralPath ( Resolve-Path -LiteralPath (Split-Path $PipelineJournal -Parent) ).Path -Force if ( -not $journalParent.PSIsContainer -or ($journalParent.Attributes -band [IO.FileAttributes]::ReparsePoint) -or [IO.Path]::GetPathRoot($journalParent.FullName).TrimEnd("\") -ine "D:" ) { throw "Pipeline journal parent must be a real D: directory" } if (-not (Test-Path -LiteralPath $PipelineJournal)) { New-Item -ItemType File -Path $PipelineJournal | Out-Null } $journal = Get-Item -LiteralPath $PipelineJournal -Force if ( $journal.PSIsContainer -or ($journal.Attributes -band [IO.FileAttributes]::ReparsePoint) ) { throw "Pipeline journal must be a regular file" } $temporaryRoot = Join-Path $env:TEMP "ndc-mission-core-telegraf-$([Guid]::NewGuid().ToString('N'))" $archivePath = Join-Path $temporaryRoot "telegraf.zip" $expandedRoot = Join-Path $temporaryRoot "expanded" try { New-Item -ItemType Directory -Path $temporaryRoot, $expandedRoot -Force | Out-Null Invoke-WebRequest -UseBasicParsing -Uri $archiveUrl -OutFile $archivePath $actualSha256 = (Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash.ToLowerInvariant() if ($actualSha256 -ne $ExpectedSha256.ToLowerInvariant()) { throw "Telegraf archive SHA256 mismatch" } Expand-Archive -LiteralPath $archivePath -DestinationPath $expandedRoot $sourceExecutable = Get-ChildItem -Path $expandedRoot -Filter "telegraf.exe" -Recurse | Select-Object -First 1 if (-not $sourceExecutable) { throw "telegraf.exe is missing from the verified archive" } New-Item -ItemType Directory -Path $installRoot, $configurationRoot -Force | Out-Null Copy-Item -LiteralPath $sourceExecutable.FullName -Destination (Join-Path $installRoot "telegraf.exe") Copy-Item -LiteralPath $ConfigurationTemplate -Destination $configurationPath Copy-Item -LiteralPath $PipelineCollector -Destination $collectorPath & icacls.exe $configurationRoot /inheritance:r /grant:r ` "*S-1-5-18:(OI)(CI)F" "*S-1-5-32-544:(OI)(CI)F" | Out-Null if ($LASTEXITCODE -ne 0) { throw "Failed to restrict the telemetry agent configuration directory" } $executable = Join-Path $installRoot "telegraf.exe" $validationOutput = Join-Path $temporaryRoot "validation.out.log" $validationError = Join-Path $temporaryRoot "validation.error.log" $validation = Start-Process -FilePath $executable ` -ArgumentList @("--config", $configurationPath, "--test") ` -NoNewWindow -Wait -PassThru ` -RedirectStandardOutput $validationOutput ` -RedirectStandardError $validationError if ($validation.ExitCode -ne 0) { $validationDetail = Get-Content -LiteralPath $validationError -Tail 8 | Out-String throw "Telegraf configuration validation failed: $validationDetail" } & $executable --service install --config $configurationPath if ($LASTEXITCODE -ne 0) { throw "Telegraf service installation failed" } $serviceEnvironment = @( "MISSIONCORE_CONTOUR_ID=$($payload.MISSIONCORE_CONTOUR_ID)", "MISSIONCORE_AGENT_ID=$($payload.MISSIONCORE_AGENT_ID)", "MISSIONCORE_NODE_ID=$($payload.MISSIONCORE_NODE_ID)", "MISSIONCORE_MQTT_HOST=$($payload.MISSIONCORE_MQTT_HOST)", "MISSIONCORE_MQTT_PORT=$($payload.MISSIONCORE_MQTT_PORT)", "MISSIONCORE_MQTT_USERNAME=$($payload.MISSIONCORE_MQTT_USERNAME)", "MISSIONCORE_MQTT_PASSWORD=$($payload.MISSIONCORE_MQTT_PASSWORD)", "MISSIONCORE_TELEMETRY_INTERVAL=$($payload.MISSIONCORE_TELEMETRY_INTERVAL)" ) $serviceRegistryPath = "HKLM:\SYSTEM\CurrentControlSet\Services\$serviceName" Set-ItemProperty -Path $serviceRegistryPath -Name Environment ` -Type MultiString -Value $serviceEnvironment Set-ItemProperty -Path $serviceRegistryPath -Name DisplayName ` -Value "NDC Mission Core Telemetry Agent" & sc.exe config $serviceName DisplayName= "NDC Mission Core Telemetry Agent" | Out-Null if ($LASTEXITCODE -ne 0) { throw "Failed to apply the NDC service display name" } Set-Service -Name $serviceName -StartupType Automatic Start-Service -Name $serviceName $service = Get-Service -Name $serviceName $service.WaitForStatus([ServiceProcess.ServiceControllerStatus]::Running, [TimeSpan]::FromSeconds(20)) [ordered]@{ SchemaVersion = "missioncore.telemetry-agent-install-result/v1" Agent = "Telegraf" Version = $Version ServiceName = $service.Name DisplayName = $service.DisplayName Status = $service.Status.ToString() StartType = $service.StartType.ToString() Configuration = $configurationPath PipelineCollector = $collectorPath PipelineJournal = $PipelineJournal Sha256 = $actualSha256 } | ConvertTo-Json -Compress } finally { Remove-Item -LiteralPath $temporaryRoot -Recurse -Force -ErrorAction SilentlyContinue }