LibreDevOpsHelpers.Splunk/LibreDevOpsHelpers.Splunk.psm1
|
Set-StrictMode -Version Latest # ------------------------------------------------------------------------------------------------- # Basic Splunk primitives. UNTESTED AGAINST A LIVE SPLUNK INSTANCE: these wrap the two most common, # well-documented Splunk REST surfaces (the HTTP Event Collector for sending events, and the search # jobs API for running searches) using only stable, long-standing endpoints. They are deliberately # small and conservative. Validate against your Splunk deployment before relying on them; treat the # tokens they take as secrets (pass from a Key Vault data source or an environment variable, never a # literal in source). # ------------------------------------------------------------------------------------------------- function Send-LdoSplunkHecEvent { <# .SYNOPSIS Sends one or more events to a Splunk HTTP Event Collector (HEC). .DESCRIPTION POSTs to the HEC /services/collector/event endpoint with the HEC token in the Authorization header (Splunk <token>). Each event is wrapped in the HEC envelope with its optional sourcetype, source, index and host. Retries transient failures through the shared retry helper. UNTESTED LIVE: verify against your HEC configuration (token, allowed indexes, and whether the endpoint is /event or /raw for your data). .PARAMETER Uri The HEC collector base URI, for example https://splunk.example.com:8088. The /services/collector/event path is appended. .PARAMETER Token The HEC token (a secret). Sent as the Authorization header value "Splunk <token>". .PARAMETER Event One or more event payloads. A string is sent as the event field; an object is sent as-is. .PARAMETER Sourcetype Optional HEC sourcetype for every event in the call. .PARAMETER Source Optional HEC source. .PARAMETER Index Optional target index. .PARAMETER Host Optional host field. .PARAMETER SkipCertificateCheck Skip TLS validation (self-signed lab HEC only; never in production). .PARAMETER MaxRetries Maximum attempts. Defaults to 5. .EXAMPLE Send-LdoSplunkHecEvent -Uri 'https://splunk:8088' -Token $t -Event @{ action = 'login'; user = 'alice' } -Sourcetype '_json' .OUTPUTS System.Object. The HEC response (typically { text = 'Success'; code = 0 }). #> [CmdletBinding()] [OutputType([object])] param( [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Uri, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Token, [Parameter(Mandatory, ValueFromPipeline)][ValidateNotNull()][object[]]$Event, [string]$Sourcetype, [string]$Source, [string]$Index, [string]$HostName, [switch]$SkipCertificateCheck, [int]$MaxRetries = 5 ) begin { $collected = [System.Collections.Generic.List[object]]::new() } process { foreach ($e in $Event) { $collected.Add($e) } } end { if ($collected.Count -eq 0) { throw 'Send-LdoSplunkHecEvent: no events to send.' } # One newline-delimited body of HEC envelopes, the documented batch shape. $envelopes = foreach ($e in $collected) { $env = [ordered]@{ event = $e } if ($Sourcetype) { $env.sourcetype = $Sourcetype } if ($Source) { $env.source = $Source } if ($Index) { $env.index = $Index } if ($HostName) { $env.host = $HostName } $env | ConvertTo-Json -Depth 20 -Compress } $body = ($envelopes -join "`n") $endpoint = "$($Uri.TrimEnd('/'))/services/collector/event" $headers = @{ Authorization = "Splunk $Token" } $rest = @{ Uri = $endpoint Method = 'Post' Headers = $headers Body = $body ContentType = 'application/json' } if ($SkipCertificateCheck) { $rest.SkipCertificateCheck = $true } Write-LdoLog -Level INFO -Message "Sending $($collected.Count) event(s) to Splunk HEC at $endpoint." Invoke-LdoWithRetry -OperationName "Splunk HEC POST $endpoint" -MaxRetries $MaxRetries -ScriptBlock { Invoke-RestMethod @rest } } } function Invoke-LdoSplunkSearch { <# .SYNOPSIS Runs a Splunk search (a blocking search job) and returns the results. .DESCRIPTION Creates a oneshot search job against the Splunk search jobs REST endpoint (/services/search/jobs) with output_mode=json, which runs the search and returns the results in a single call, and returns the parsed results. Authenticates with a bearer token (a Splunk auth token or session key). UNTESTED LIVE: verify the endpoint, the token type your deployment expects, and that the search language and app context match your Splunk. .PARAMETER Uri The Splunk management URI, for example https://splunk.example.com:8089. .PARAMETER Token The Splunk authentication token (a secret), sent as "Bearer <token>". .PARAMETER Search The SPL search string. A leading "search" is added when the query does not already begin with a generating command. .PARAMETER EarliestTime Optional earliest time modifier (for example -24h, or an absolute time). .PARAMETER LatestTime Optional latest time modifier (for example now). .PARAMETER MaxCount Maximum result rows. Defaults to 100. .PARAMETER SkipCertificateCheck Skip TLS validation (self-signed lab only). .PARAMETER MaxRetries Maximum attempts. Defaults to 5. .EXAMPLE Invoke-LdoSplunkSearch -Uri 'https://splunk:8089' -Token $t -Search 'index=main error' -EarliestTime '-1h' .OUTPUTS System.Object[]. The search result rows. #> [CmdletBinding()] [OutputType([object[]])] param( [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Uri, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Token, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Search, [string]$EarliestTime, [string]$LatestTime, [int]$MaxCount = 100, [switch]$SkipCertificateCheck, [int]$MaxRetries = 5 ) # Splunk requires the search to start with a search command; prepend "search" unless the query # already starts with a generating or explicit command (| or "search"). $spl = $Search.TrimStart() if (-not ($spl -match '^\s*(search\b|\|)')) { $spl = "search $spl" } $form = @{ search = $spl output_mode = 'json' exec_mode = 'oneshot' count = $MaxCount } if ($EarliestTime) { $form.earliest_time = $EarliestTime } if ($LatestTime) { $form.latest_time = $LatestTime } $endpoint = "$($Uri.TrimEnd('/'))/services/search/jobs" $rest = @{ Uri = $endpoint Method = 'Post' Headers = @{ Authorization = "Bearer $Token" } Body = $form } if ($SkipCertificateCheck) { $rest.SkipCertificateCheck = $true } Write-LdoLog -Level INFO -Message "Running Splunk oneshot search against $endpoint." $response = Invoke-LdoWithRetry -OperationName "Splunk search POST $endpoint" -MaxRetries $MaxRetries -ScriptBlock { Invoke-RestMethod @rest } # A oneshot json response carries the rows under .results; guard for an empty or shapeless # response so callers always get an array. if ($response -and $response.PSObject.Properties['results']) { return @($response.results) } return @() } function Test-LdoSplunkConnection { <# .SYNOPSIS Checks reachability and authentication against a Splunk management endpoint. .DESCRIPTION GETs /services/server/info with the supplied token and returns true when the call succeeds. A cheap liveness and auth check before running searches. UNTESTED LIVE. .PARAMETER Uri The Splunk management URI. .PARAMETER Token The Splunk authentication token (a secret), sent as "Bearer <token>". .PARAMETER SkipCertificateCheck Skip TLS validation (self-signed lab only). .EXAMPLE Test-LdoSplunkConnection -Uri 'https://splunk:8089' -Token $t .OUTPUTS System.Boolean #> [CmdletBinding()] [OutputType([bool])] param( [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Uri, [Parameter(Mandatory)][ValidateNotNullOrEmpty()][string]$Token, [switch]$SkipCertificateCheck ) $endpoint = "$($Uri.TrimEnd('/'))/services/server/info?output_mode=json" $rest = @{ Uri = $endpoint Method = 'Get' Headers = @{ Authorization = "Bearer $Token" } } if ($SkipCertificateCheck) { $rest.SkipCertificateCheck = $true } try { Invoke-RestMethod @rest | Out-Null Write-LdoLog -Level SUCCESS -Message "Splunk reachable and authenticated at $Uri." return $true } catch { Write-LdoLog -Level ERROR -Message "Splunk connection check failed for ${Uri}: $($_.Exception.Message)" return $false } } |