Scripts/Update-OpenEndpointEventsConfig.ps1
|
<#
.SYNOPSIS Refreshes the local OpenEndpointEvents uploader configuration from a protected HTTPS config blob. .DESCRIPTION Downloads a remote uploader-config.json file, validates the content, writes it locally, locks down ACLs, and logs success or failure using OpenEndpointEvents. .PARAMETER RefreshConfigPath Local config-refresh.json path. .PARAMETER Force Bypasses MinimumRefreshIntervalHours. .PARAMETER StartUploaderAfterUpdate Starts the upload scheduled task after a successful config update. .EXAMPLE .\Update-OpenEndpointEventsConfig.ps1 -Force -StartUploaderAfterUpdate #> [CmdletBinding()] param( [string]$RefreshConfigPath = "C:\ProgramData\OpenEndpointEvents\Config\config-refresh.json", [switch]$Force, [switch]$StartUploaderAfterUpdate ) $ErrorActionPreference = "Stop" function Set-OpenEndpointEventsConfigAcl { param( [Parameter(Mandatory = $true)] [string]$Path ) if (-not (Test-Path -Path $Path)) { New-Item -Path $Path -ItemType Directory -Force | Out-Null } icacls $Path /inheritance:r | Out-Null icacls $Path /grant:r "SYSTEM:(OI)(CI)(F)" | Out-Null icacls $Path /grant:r "Administrators:(OI)(CI)(F)" | Out-Null icacls $Path /remove "Users" "Authenticated Users" "Everyone" 2>$null | Out-Null } function Write-ConfigInfo { param( [string]$EventName, [string]$Message, [hashtable]$Data ) Import-Module OpenEndpointEvents -ErrorAction SilentlyContinue if (Get-Command Write-EndpointInfo -ErrorAction SilentlyContinue) { Write-EndpointInfo ` -Source "OpenEndpointEventsConfig" ` -EventName $EventName ` -Message $Message ` -IncludeEndpointIdentity ` -Data $Data | Out-Null } } function Write-ConfigError { param( [string]$EventName, [string]$Message, [System.Management.Automation.ErrorRecord]$ErrorRecord, [hashtable]$Data ) Import-Module OpenEndpointEvents -ErrorAction SilentlyContinue if (Get-Command Write-EndpointError -ErrorAction SilentlyContinue) { Write-EndpointError ` -Source "OpenEndpointEventsConfig" ` -EventName $EventName ` -Message $Message ` -ErrorRecord $ErrorRecord ` -IncludeEndpointIdentity ` -Data $Data | Out-Null } } try { [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 if (-not (Test-Path -Path $RefreshConfigPath)) { throw "Refresh config file not found: $RefreshConfigPath" } $refreshConfig = Get-Content -Path $RefreshConfigPath -Raw | ConvertFrom-Json $configUri = [string]$refreshConfig.ConfigUri $localConfigPath = [string]$refreshConfig.LocalConfigPath $statePath = [string]$refreshConfig.StatePath $minimumRefreshIntervalHours = [int]$refreshConfig.MinimumRefreshIntervalHours if ([string]::IsNullOrWhiteSpace($configUri)) { throw "ConfigUri is missing from $RefreshConfigPath" } if ($configUri -notmatch "^https://") { throw "ConfigUri must use HTTPS." } if ([string]::IsNullOrWhiteSpace($localConfigPath)) { $localConfigPath = "C:\ProgramData\OpenEndpointEvents\Config\uploader-config.json" } if ([string]::IsNullOrWhiteSpace($statePath)) { $statePath = "C:\ProgramData\OpenEndpointEvents\State\config-refresh-state.json" } $localConfigRoot = Split-Path -Path $localConfigPath -Parent $stateRoot = Split-Path -Path $statePath -Parent New-Item -Path $localConfigRoot -ItemType Directory -Force | Out-Null New-Item -Path $stateRoot -ItemType Directory -Force | Out-Null Set-OpenEndpointEventsConfigAcl -Path $localConfigRoot $state = $null if (Test-Path -Path $statePath) { $state = Get-Content -Path $statePath -Raw | ConvertFrom-Json } if (-not $Force -and $state -and $state.LastRefreshUtc -and $minimumRefreshIntervalHours -gt 0) { $lastRefresh = [datetime]$state.LastRefreshUtc $nextAllowed = $lastRefresh.ToUniversalTime().AddHours($minimumRefreshIntervalHours) if ((Get-Date).ToUniversalTime() -lt $nextAllowed) { Write-ConfigInfo ` -EventName "ConfigRefreshSkipped" ` -Message "OpenEndpointEvents config refresh skipped due to minimum refresh interval" ` -Data @{ RefreshConfigPath = $RefreshConfigPath LocalConfigPath = $localConfigPath LastRefreshUtc = $lastRefresh.ToUniversalTime().ToString("o") NextAllowedUtc = $nextAllowed.ToString("o") Status = "Skipped" } exit 0 } } $tempPath = Join-Path $env:TEMP "uploader-config-$([guid]::NewGuid()).json" try { $response = Invoke-WebRequest ` -Uri $configUri ` -OutFile $tempPath ` -UseBasicParsing ` -ErrorAction Stop $downloadedConfigRaw = Get-Content -Path $tempPath -Raw $downloadedConfig = $downloadedConfigRaw | ConvertFrom-Json if ([string]::IsNullOrWhiteSpace($downloadedConfig.ContainerSasUrl)) { throw "Downloaded uploader config does not contain ContainerSasUrl." } if ($downloadedConfig.ContainerSasUrl -notmatch "^https://") { throw "ContainerSasUrl must use HTTPS." } if ($downloadedConfig.ContainerSasUrl -notmatch "\?") { throw "ContainerSasUrl does not contain a SAS query string." } if ([string]::IsNullOrWhiteSpace($downloadedConfig.LogRoot)) { throw "Downloaded uploader config does not contain LogRoot." } if ([string]::IsNullOrWhiteSpace($downloadedConfig.StateRoot)) { throw "Downloaded uploader config does not contain StateRoot." } if ([string]::IsNullOrWhiteSpace($downloadedConfig.BlobPrefix)) { throw "Downloaded uploader config does not contain BlobPrefix." } if ($downloadedConfig.UploadSasExpiresUtc) { $expiry = [datetime]$downloadedConfig.UploadSasExpiresUtc if ($expiry.ToUniversalTime() -lt (Get-Date).ToUniversalTime()) { throw "Downloaded upload SAS is already expired. UploadSasExpiresUtc=$($expiry.ToUniversalTime().ToString('o'))" } } Copy-Item -Path $tempPath -Destination $localConfigPath -Force Set-OpenEndpointEventsConfigAcl -Path $localConfigRoot $hash = (Get-FileHash -Path $localConfigPath -Algorithm SHA256).Hash $newState = [ordered]@{ LastRefreshUtc = (Get-Date).ToUniversalTime().ToString("o") LocalConfigPath = $localConfigPath ConfigVersion = $downloadedConfig.ConfigVersion UploadSasExpiresUtc = $downloadedConfig.UploadSasExpiresUtc Sha256 = $hash StatusCode = [int]$response.StatusCode Status = "Success" } $newState | ConvertTo-Json -Depth 10 | Set-Content -Path $statePath -Encoding UTF8 Write-ConfigInfo ` -EventName "ConfigUpdated" ` -Message "OpenEndpointEvents uploader config updated from remote config blob" ` -Data @{ RefreshConfigPath = $RefreshConfigPath LocalConfigPath = $localConfigPath ConfigVersion = $downloadedConfig.ConfigVersion UploadSasExpiresUtc = $downloadedConfig.UploadSasExpiresUtc Sha256 = $hash Status = "Success" } if ($StartUploaderAfterUpdate -or [bool]$refreshConfig.RestartUploadTaskAfterUpdate) { Start-ScheduledTask -TaskName "OpenEndpointEvents Upload" -ErrorAction SilentlyContinue } } finally { Remove-Item -Path $tempPath -Force -ErrorAction SilentlyContinue } exit 0 } catch { Write-ConfigError ` -EventName "ConfigUpdateFailed" ` -Message "OpenEndpointEvents uploader config update failed" ` -ErrorRecord $_ ` -Data @{ RefreshConfigPath = $RefreshConfigPath Status = "Failed" } exit 1 } |