Public/Write-Influx.ps1

function Write-Influx {
    <#
        .SYNOPSIS
            Writes data to Influx via the REST API.
 
        .DESCRIPTION
            Use to send data in to an Influx database by providing a hashtable of tags and values.
 
        .PARAMETER InputObject
            A metric object (generated by one of the Get-*Metric cmdlets from this module) which can be provided as pipeline input.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            A hashtable of tag names and values.
 
        .PARAMETER Metrics
            A hashtable of metric names and values.
 
        .PARAMETER Timestamp
            Specify the exact date and time for the measure data point. If not specified the current date and time is used.
 
        .PARAMETER Server
            The URL and port for the Influx REST API. Default: 'http://localhost:8086'
 
        .PARAMETER Database
            The name of the Influx database to write to. (This is an InfluxDB v1.x Parameter)
 
        .PARAMETER Credential
            A PSCredential object with the username and password to use if the Influx server has authentication enabled. (This is an InfluxDB v1.x Parameter)
         
        .PARAMETER Bucket
            The name of the Influx bucket/database to write to. (This is an InfluxDB v2.x Parameter)
 
        .PARAMETER Token
            The token to authenticate with InfluxDB. (This is an InfluxDB v2.x Parameter)
 
        .PARAMETER Organisation
            The name of the Influx organisation. (This is an InfluxDB v2.x Parameter)
    
        .PARAMETER Bulk
            Switch: Use to have all metrics transmitted via a single connection to Influx.
 
        .PARAMETER BulkSize
            The number of metrics to include when using the -Bulk switch before a write occurs. Default: 5000.
 
        .PARAMETER ExcludeEmptyMetric
            Switch: Use to exclude null or empty metric values from being sent. Useful where a metric is initially created as an integer but then
            an empty or null instance of that metric would attempt to be sent as an empty string, resulting in a datatype conflict.
 
        .PARAMETER TrustServerCertificate
            Switch: Skips Server SSL certificate validation.
 
        .PARAMETER SingleLineMetrics
            Switch: Sends all measured values for every Metric object or Measure passed within the single Influx Line Protocol line.
 
        .EXAMPLE
            Write-Influx -Measure WebServer -Tags @{Server='Host01'} -Metrics @{CPU=100; Memory=50} -Database Web -Server http://myinflux.local:8086
             
            Description
            -----------
            This command will submit the provided tag and metric data for a measure called 'WebServer' to a database called 'Web'
            via the API endpoint 'http://myinflux.local:8086'
    #>

    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium', DefaultParameterSetName = 'Measure_v1')]
    param (
        [Parameter(ParameterSetName = 'MetricObject_v1', Mandatory, ValueFromPipeline)]
        [Parameter(ParameterSetName = 'MetricObject_v2', Mandatory, ValueFromPipeline)]
        [PSTypeName('Metric')]
        [PSObject[]]
        $InputObject,

        [Parameter(ParameterSetName = 'Measure_v1', Mandatory)]
        [Parameter(ParameterSetName = 'Measure_v2', Mandatory)]
        [string]
        $Measure,

        [Parameter(ParameterSetName = 'Measure_v1')]
        [Parameter(ParameterSetName = 'Measure_v2')]
        [hashtable]
        $Tags,
        
        [Parameter(ParameterSetName = 'Measure_v1', Mandatory)]
        [Parameter(ParameterSetName = 'Measure_v2', Mandatory)]
        [hashtable]
        $Metrics,

        [Parameter(ParameterSetName = 'Measure_v1')]
        [Parameter(ParameterSetName = 'Measure_v2')]
        [datetime]
        $TimeStamp,
        
        [string]
        $Server = 'http://localhost:8086',

        [switch]
        $Bulk,

        [int]
        $BulkSize = 5000,

        [switch]
        $ExcludeEmptyMetric,

        [Parameter(ParameterSetName = 'Measure_v1', Mandatory)]
        [Parameter(ParameterSetName = 'MetricObject_v1', Mandatory)]
        [string]
        $Database,

        [Parameter(ParameterSetName = 'Measure_v1')]
        [Parameter(ParameterSetName = 'MetricObject_v1')]
        [pscredential]
        $Credential,

        [Parameter(ParameterSetName = 'Measure_v2', Mandatory)]
        [Parameter(ParameterSetName = 'MetricObject_v2', Mandatory)]
        [string]
        $Organisation,

        [Parameter(ParameterSetName = 'Measure_v2', Mandatory)]
        [Parameter(ParameterSetName = 'MetricObject_v2', Mandatory)]
        [string]
        $Bucket,

        [Parameter(ParameterSetName = 'Measure_v2', Mandatory)]
        [Parameter(ParameterSetName = 'MetricObject_v2', Mandatory)]
        [string]
        $Token,

        [switch]
        $TrustServerCertificate,

        [switch]
        $SingleLineMetrics
    )

    begin {
        if ($Credential) {
            $Username = $Credential.UserName
            $Password = $Credential.GetNetworkCredential().Password

            $EncodedCreds = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes("$($Username):$($Password)"))

            $Headers = @{
                Authorization = "Basic $EncodedCreds"
            }
        }

        if ($TrustServerCertificate) {

            $SkipCertificateCheck = $false

            if (Get-Help Invoke-RestMethod -Parameter SkipCertificateCheck -ErrorAction SilentlyContinue) {
                $SkipCertificateCheck = $true
            }
            else {
                [System.Net.ServicePointManager]::ServerCertificateValidationCallback = { $true }
            }
        }

        if ($Database) {
            $URI = "$Server/write?&db=$Database"
        }
        else {
            $Headers = @{
                Authorization = "Token $Token"
            }
            
            $URI = "$Server/api/v2/write?org=$Organisation&bucket=$Bucket"
        }

        $BulkCount = 0
        $BulkBody = @()
    }
    process {
        if (-not $InputObject) {
            $InputObject = @{
                Measure   = $Measure
                Metrics   = $Metrics
                Tags      = $Tags
                TimeStamp = $TimeStamp
            }
        }

        foreach ($MetricObject in $InputObject) {

            if ($MetricObject.TimeStamp) {
                $timeStampNanoSecs = $MetricObject.Timestamp | ConvertTo-UnixTimeNanosecond
            }
            else {
                $null = $timeStampNanoSecs
            }

            if (($MetricObject.Tags).count -ne 0) {

                $TagData = foreach ($Tag in $MetricObject.Tags.Keys) {
                    if ([string]::IsNullOrEmpty($MetricObject.Tags[$Tag])) {
                        Write-Warning "$Tag skipped as it's value was null or empty, which is not permitted by InfluxDB."
                    }
                    else {
                        "$($Tag | Out-InfluxEscapeString)=$($MetricObject.Tags[$Tag] | Out-InfluxEscapeString)"
                    }
                }
                $TagData = $TagData -Join ','
                $TagData = ",$TagData"
            }
 
            if ($SingleLineMetrics) {

                $MetricData = foreach ($Metric in $MetricObject.Metrics.Keys) {
                    if ($ExcludeEmptyMetric -and [string]::IsNullOrEmpty($MetricObject.Metrics[$Metric])) {
                        Write-Verbose "$Metric skipped as -ExcludeEmptyMetric was specified and the value is null or empty."
                    }
                    else {
                        if ($MetricObject.Metrics[$Metric] -isnot [ValueType]) { 
                            "$($Metric | Out-InfluxEscapeString)=""$($MetricObject.Metrics[$Metric])"""
                        }
                        else {
                            "$($Metric | Out-InfluxEscapeString)=$($MetricObject.Metrics[$Metric] | Out-InfluxEscapeString)"
                        }
                    }
                }
                $MetricData = $MetricData -Join ','

                $Body = "$($MetricObject.Measure | Out-InfluxEscapeString)$TagData $MetricData $timeStampNanoSecs"
            }
            else {

                $Body = foreach ($Metric in $MetricObject.Metrics.Keys) {
            
                    if ($ExcludeEmptyMetric -and [string]::IsNullOrEmpty($MetricObject.Metrics[$Metric])) {
                        Write-Verbose "$Metric skipped as -ExcludeEmptyMetric was specified and the value is null or empty."
                    }
                    else {
                        if ($MetricObject.Metrics[$Metric] -isnot [ValueType]) { 
                            $MetricValue = '"' + $MetricObject.Metrics[$Metric] + '"'
                        }
                        else {
                            $MetricValue = $MetricObject.Metrics[$Metric] | Out-InfluxEscapeString
                        }
                
                        "$($MetricObject.Measure | Out-InfluxEscapeString)$TagData $($Metric | Out-InfluxEscapeString)=$MetricValue $timeStampNanoSecs"
                    }            
                }
            }

            $InvokeRestMethod = @{
                Uri    = $Uri
                Method = 'Post'
            }

            if ($SkipCertificateCheck) {
                $InvokeRestMethod.add('SkipCertificateCheck', $true)
            }

            if ($Body) {
                $Body = $Body -Join "`n"
            
                If ($Bulk) {

                    $BulkCount++
                    $BulkBody += $Body

                    if ($BulkCount -eq $BulkSize) {
                        Write-Verbose "BulkSize of $BulkSize lines reached."
                        
                        $BulkBody = $BulkBody -Join "`n"

                        if ($PSCmdlet.ShouldProcess($URI, $BulkBody)) {
                            Invoke-RestMethod @InvokeRestMethod -Body $BulkBody -Headers $Headers | Out-Null
                        }

                        $BulkBody = @()
                        $BulkCount = 0
                    }
                }
                else {
                    if ($PSCmdlet.ShouldProcess($URI, $Body)) {
                        Invoke-RestMethod @InvokeRestMethod -Body $Body -Headers $Headers | Out-Null
                    }
                }
            
            }
        }
        
    }
    end {
        if ($Bulk) {
            $BulkBody = $BulkBody -Join "`n"
            
            if ($PSCmdlet.ShouldProcess($URI, $BulkBody)) {
                Invoke-RestMethod @InvokeRestMethod -Body $BulkBody -Headers $Headers | Out-Null
            }
        }

        if ($TrustServerCertificate) {
            [System.Net.ServicePointManager]::ServerCertificateValidationCallback = $null
        }
    }
}