Influx.psm1

Function Get-3ParSystemMetric {
    <#
        .SYNOPSIS
            Returns 3Par System metrics as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            This function requires the HPE3PARPSToolkit module from HP.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER Tags
            An array of 3PAR system tags to be included, from those returned by Get-3ParSystem.
 
        .PARAMETER SANIPAddress
            The IP address of the 3PAR SAN to be queried.
 
        .PARAMETER SANUserName
            The username for connecting to the 3PAR.
 
        .PARAMETER SANPwdFile
            The encrypted password file for connecting to the 3PAR. This should be created with Set-3parPoshSshConnectionPasswordFile.
 
        .EXAMPLE
            Get-3ParSystemMetric -Measure 'Test3PAR' -Tags System_Name,System_Model,System_ID -SANIPAddress 1.2.3.4 -SANUsername admin -SANPwdFile C:\scripts\3par.pwd
             
            Description
            -----------
            This command will return a metric object with the specified tags and 3PAR metrics for a measure called 'Test3PAR'.
    #>

    [cmdletbinding()]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Tags', Justification = 'Used inside the Where-Object closure below; PSScriptAnalyzer does not see usage inside nested scriptblocks.')]
    param(
        [String]
        $Measure = '3PARSystem',

        [String[]]
        $Tags = ('System_Name', 'System_Model'),

        [Parameter(Mandatory = $true)]
        [String]
        $SANIPAddress,

        [Parameter(Mandatory = $true)]
        [String]
        $SANUserName, 

        [Parameter(Mandatory = $true)]
        [String]
        $SANPwdFile
    )

    try {
        Import-Module HPE3PARPSToolkit -ErrorAction Stop

        Set-3parPoshSshConnectionUsingPasswordFile -SANIPAddress $SANIPAddress -SANUserName $SANUserName -epwdFile $SANPwdFile -ErrorAction Stop | Out-Null
    }
    catch {
        throw $_
    }
    
    $3Par = Get-3parSystem
    
    if ($3Par) {
    
        $TagData = @{}
        $3Par.GetEnumerator() | Where-Object {$_.Name -in $Tags} | ForEach-Object {
            if ($_.Value) {
                $TagData.Add($_.Name, $_.Value)
            }
        }
        
        $3ParSpace = Get-3parSpace
        
        [pscustomobject]@{
            PSTypeName = 'Metric'
            Measure    = $Measure
            Tags       = $TagData
            Metrics    = @{ 
                System_RawFreeMB    = [float]$3ParSpace."RawFree(MB)"
                System_UsableFreeMB = [float]$3ParSpace."UsableFree(MB)"
            }
        }
    }
    else {
        Write-Verbose 'No 3par system data returned'
    }
}
Function Get-3ParVirtualVolumeMetric {
    <#
        .SYNOPSIS
            Returns the 3Par Virtual Volume metrics (as returned by Get-3parStatVV) as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            This function requires the HPE3PARPSToolkit module from HP.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER SANIPAddress
            The IP address of the 3PAR SAN to be queried.
 
        .PARAMETER SANUserName
            The username for connecting to the 3PAR.
 
        .PARAMETER SANPwdFile
            The encrypted password file for connecting to the 3PAR. This should be created with Set-3parPoshSshConnectionPasswordFile.
 
        .EXAMPLE
            Get-3ParVirtualVolumeMetric -Measure 'Test3PARVV' -SANIPAddress 1.2.3.4 -SANUsername admin -SANPwdFile C:\scripts\3par.pwd
             
            Description
            -----------
            This command will return a PowerShell object with the 3PAR Virtual Volume metrics for a measure called 'Test3PARVV'.
    #>
  
    [cmdletbinding()]
    param(
        [String]
        $Measure = '3PARVirtualVolume',

        [Parameter(Mandatory = $true)]
        [String]
        $SANIPAddress,

        [Parameter(Mandatory = $true)]
        [String]
        $SANUserName, 

        [Parameter(Mandatory = $true)]
        [String]
        $SANPwdFile
    )

    try {
        Import-Module HPE3PARPSToolkit -ErrorAction Stop

        Set-3parPoshSshConnectionUsingPasswordFile -SANIPAddress $SANIPAddress -SANUserName $SANUserName -epwdFile $SANPwdFile -ErrorAction Stop | Out-Null
    }
    catch {
        throw $_
    }
    
    $3Par = Get-3parSystem
    
    if ($3Par) {
    
        $VVStats = (Get-3parStatVV -Iteration 1) | Where-Object {$_.VVname -notin 'admin', '.srdata'}
        
        if ($VVStats) {

            ForEach ($VV in $VVStats) {

                $TagData = @{
                    System_Name = $3Par.System_Name
                    VVname      = $VV.VVname
                }
        
                $Metrics = @{}

                $VV.PSObject.Properties | Where-Object {$_.Name -notin 'VVname', 'Time', 'Date', 'r/w'} | ForEach-Object {
                    if ($_.Value) {
                        $Metrics.Add($_.Name, [float]$_.Value)
                    }
                }   
            
                [pscustomobject]@{
                    PSTypeName = 'Metric'
                    Measure    = $Measure
                    Tags       = $TagData
                    Metrics    = $Metrics
                }
            }
        }
        else {
            Write-Verbose 'No Virtual Volume data returned'
        }
    }
    else {
        Write-Verbose 'No 3par system data returned'
    }
}
Function Send-3ParSystemMetric {
    <#
        .SYNOPSIS
            Sends 3Par System metrics to Influx.
 
        .DESCRIPTION
            This function requires the HPE3PARPSToolkit module from HP.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            An array of 3PAR system tags to be included, from those returned by Get-3ParSystem.
 
        .PARAMETER SANIPAddress
            The IP address of the 3PAR SAN to be queried.
 
        .PARAMETER SANUserName
            The username for connecting to the 3PAR.
 
        .PARAMETER SANPwdFile
            The encrypted password file for connecting to the 3PAR. This should be created with Set-3parPoshSshConnectionPasswordFile.
 
        .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. Default: 'storage'. This must exist in Influx!
 
        .EXAMPLE
            Send-3ParSystemMetric -Measure 'Test3PAR' -Tags System_Name,System_Model,System_ID -SANIPAddress 1.2.3.4 -SANUsername admin -SANPwdFile C:\scripts\3par.pwd
             
            Description
            -----------
            This command will submit the specified tags and 3PAR metrics to a measure called 'Test3PAR'.
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = '3PARSystem',

        [String[]]
        $Tags = ('System_Name', 'System_Model'),

        [Parameter(Mandatory = $true)]
        [String]
        $SANIPAddress,

        [Parameter(Mandatory = $true)]
        [String]
        $SANUserName, 

        [Parameter(Mandatory = $true)]
        [String]
        $SANPwdFile,

        [string]
        $Database = 'storage',
        
        [string]
        $Server = 'http://localhost:8086'

    )

    $MetricParams = @{
        Measure      = $Measure
        Tags         = $Tags
        SANIPAddress = $SANIPAddress
        SANUserName  = $SANUserName
        SANPwdFile   = $SANPwdFile
    }

    $Metric = Get-3ParSystemMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Send-3ParVirtualVolumeMetric {
    <#
        .SYNOPSIS
            Sends the 3Par Virtual Volume metrics returned by Get-3parStatVV to Influx.
 
        .DESCRIPTION
            This function requires the HPE3PARPSToolkit module from HP.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER SANIPAddress
            The IP address of the 3PAR SAN to be queried.
 
        .PARAMETER SANUserName
            The username for connecting to the 3PAR.
 
        .PARAMETER SANPwdFile
            The encrypted password file for connecting to the 3PAR. This should be created with Set-3parPoshSshConnectionPasswordFile.
 
        .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. Default: 'storage'. This must exist in Influx!
 
        .EXAMPLE
            Send-3ParVirtualVolumeMetric -Measure 'Test3PARVV' -SANIPAddress 1.2.3.4 -SANUsername admin -SANPwdFile C:\scripts\3par.pwd
             
            Description
            -----------
            This command will submit the 3PAR Virtual Volume metrics to a measure called 'Test3PARVV'.
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = '3PARVirtualVolume',

        [Parameter(Mandatory = $true)]
        [String]
        $SANIPAddress,

        [Parameter(Mandatory = $true)]
        [String]
        $SANUserName, 

        [Parameter(Mandatory = $true)]
        [String]
        $SANPwdFile,

        [string]
        $Database = 'storage',
        
        [string]
        $Server = 'http://localhost:8086'

    )
   
    $MetricParams = @{
        Measure      = $Measure
        SANIPAddress = $SANIPAddress
        SANUserName  = $SANUserName
        SANPwdFile   = $SANPwdFile
    }

    $Metric = Get-3ParVirtualVolumeMetric @MetricParams

    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Get-IsilonStoragePoolMetric {
    <#
        .SYNOPSIS
            Returns Isilon Storage Pool usage metrics returned by the Get-isiStoragepools cmdlet as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            This function requires the IsilonPlatform module from the PSGallery.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER IsilonName
            The name or IP address of the Isilon to be queried.
         
        .PARAMETER IsilonPwdFile
            The encrypted credentials file for connecting to the Isilon. This should be created with Get-Credential | Export-Clixml.
         
        .PARAMETER ClusterName
            A descriptive name for the Isilon Cluster. This can be anything and is used for the Cluster tag field.
 
        .EXAMPLE
            Get-IsilonStoragePoolMetric -Measure 'TestIsilonSP' -IsilonName 1.2.3.4 -IsilonPwdFile C:\scripts\Isilon.pwd -ClusterName TestLab
             
            Description
            -----------
            This command will return a PowerShell object with the specified Isilon's Storage Pool metrics for a measure called 'TestIsilonSP'.
    #>
  
    [cmdletbinding()]
    param(
        [String]
        $Measure = 'IsilonStoragePool',

        [Parameter(Mandatory = $true)]
        [String]
        $IsilonName,

        [Parameter(Mandatory = $true)]
        [String]
        $IsilonPwdFile,

        [Parameter(Mandatory = $true)]
        [String]
        $ClusterName

    )

    Try {
        Import-Module IsilonPlatform -ErrorAction Stop

        New-isiSession -ComputerName $IsilonName -Credential ($IsilonPwdFile | Import-Clixml) -Cluster $ClusterName
    }
    Catch {
        Throw $_
    }
    
    $StoragePools = Get-isiStoragepools
    
    if ($StoragePools) {
    
        ForEach ($StoragePool in $StoragePools) {

            $TagData = @{
                Name        = $IsilonName
                Cluster     = $ClusterName
                StoragePool = $StoragePool.name
                Id          = $StoragePool.id
            }
        
            $Metrics = @{}

            $StoragePool.usage.PSObject.Properties | Where-Object {$_.Name -notin 'balanced'} | ForEach-Object {
                if ($_.Value) {
                    $Metrics.Add($_.Name, [long]$_.Value)
                }
            }
            
            [pscustomobject]@{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = $Metrics
            }
        }
    }
    else {
        Write-Verbose 'No Storage Pool data returned'
    }

    Remove-isiSession -Cluster $ClusterName
}
Function Send-IsilonStoragePoolMetric {
    <#
        .SYNOPSIS
            Sends Isilon Storage Pool usage metrics returned by the Get-isiStoragepools cmdlet from the IsilonPlatform module to Influx.
 
        .DESCRIPTION
            This function requires the IsilonPlatform module from the PSGallery.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER IsilonName
            The name or IP address of the Isilon to be queried.
         
        .PARAMETER IsilonPwdFile
            The encrypted credentials file for connecting to the Isilon. This should be created with Get-Credential | Export-Clixml.
         
        .PARAMETER ClusterName
            A descriptive name for the Isilon Cluster. This can be anything and is used for the Cluster tag field.
 
        .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. Default: 'storage'. This must exist in Influx!
 
        .EXAMPLE
            Send-IsilonStoragePoolMetric -Measure 'TestIsilonSP' -IsilonName 1.2.3.4 -IsilonPwdFile C:\scripts\Isilon.pwd -ClusterName TestLab
             
            Description
            -----------
            This command will submit the specified Isilon's Storage Pool metrics to a measure called 'TestIsilonSP'.
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = 'IsilonStoragePool',

        [Parameter(Mandatory = $true)]
        [String]
        $IsilonName,

        [Parameter(Mandatory = $true)]
        [String]
        $IsilonPwdFile,

        [Parameter(Mandatory = $true)]
        [String]
        $ClusterName,

        [string]
        $Database = 'storage',
        
        [string]
        $Server = 'http://localhost:8086'

    )

    $MetricParams = @{
        Measure       = $Measure
        IsilonName    = $IsilonName
        IsilonPwdFile = $IsilonPwdFile
        ClusterName   = $ClusterName
    }

    $Metric = Get-IsilonStoragePoolMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Get-TFSBuildMetric {
    <#
        .SYNOPSIS
            Returns TFS Build metrics as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            This function requires the TFS module (install via Install-Module TFS).
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER Tags
            An array of Build definition properties to be included, from those returned by Get-TfsBuildDefinitions.
         
        .PARAMETER Top
            An integer defining the number of most recent builds to return. Default: 100.
         
        .PARAMETER Latest
            Switch parameter. When used returns only the most recent build for each distinct definition.
 
        .PARAMETER TFSRootURL
            The root URL for TFS, e.g https://yourserver.yoursite.com/TFS
         
        .PARAMETER TFSCollection
            The name of the TFS collection to query.
         
        .PARAMETER TFSProject
            The name of the TFS project to query.
     
        .EXAMPLE
            Get-TFSBuildMetric -Measure 'TestTFS' -Tags Name,Author -TFSRootURL https://localhost:8088/tfs -TFSCollection MyCollection -TFSProject MyProject
             
            Description
            -----------
            This command will return the specified tags and build metrics of a measure called 'TestTFS' as a PowerShell object.
    #>
  
    [cmdletbinding()]
    param(
        [String]
        $Measure = 'TFSBuild',

        [String[]]
        $Tags = ('Definition', 'Id', 'Result'),

        [int]
        $Top = 100,

        [switch]
        $Latest,

        [Parameter(Mandatory = $true)]
        [string]
        $TFSRootURL,
        
        [Parameter(Mandatory = $true)]
        [string]
        $TFSCollection,
        
        [Parameter(Mandatory = $true)]
        [string]
        $TFSProject
    )

    try {    
        Import-Module TFS -ErrorAction Stop
    }
    catch {
        throw $_
    }
    
    $global:tfs = @{
        root_url   = $TFSRootURL
        collection = $TFSCollection
        project    = $TFSProject
    }
    
    Write-Verbose "TFS settings:`n`n$($global:tfs)"

    Write-Verbose "`nGetting builds.."
    $Builds = Get-TFSBuilds -Top $Top | Where-Object { $_.StartTime }

    If ($Latest) {
        $Builds = $Builds | Group-Object Definition | ForEach-Object { $_.Group | Sort-Object StartTime -Descending | Select-Object -First 1 }
    }

    if ($Builds) {
    
        ForEach ($Build in $Builds) { 
         
            $TagData = @{
                Collection  = $TFSCollection
                Project     = $TFSProject
                RequestedBy = $Build.raw.requestedBy.displayname
            }

            ($Build | Select-Object $Tags).PsObject.Properties | ForEach-Object {
                if ($_.Value) {
                    $TagData.Add($_.Name, $_.Value)
                }
            }
                
            #Used to support row highlighting for non-successful builds
            $ResultNumeric = Switch ($Build.Result) {
                'partiallySucceeded' { 1 }
                'failed' { 2 }
                default { $null }
            }
                
            $Metrics = @{
                Name          = $Build.Definition
                Result        = $Build.Result
                ResultNumeric = $ResultNumeric
                Duration      = $Build.Duration
                sourceBranch  = $Build.raw.sourceBranch
                sourceVersion = $Build.raw.sourceVersion
                Id            = $Build.Id
                RequestedBy   = $Build.raw.requestedBy.displayname
            }

            'StartTime', 'FinishTime' | ForEach-Object {
                If ($Build.$_ -is [datetime]) {
                    $Metrics.Add($_, ($Build.$_ | ConvertTo-UnixTimeMillisecond)) 
                }
            }
            
            [pscustomobject]@{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = $Metrics
                TimeStamp  = $Build.StartTime
            }
        } 
    }
    else {
        Write-Verbose 'No build data returned'
    }
}
Function Send-TFSBuildMetric {
    <#
        .SYNOPSIS
            Sends TFS Build metrics to Influx.
 
        .DESCRIPTION
            This function requires the TFS module (install via Install-Module TFS).
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            An array of Build definition properties to be included, from those returned by Get-TfsBuildDefinitions.
         
        .PARAMETER Top
            An integer defining the number of most recent builds to return. Default: 100.
         
        .PARAMETER Latest
            Switch parameter. When used returns only the most recent build for each distinct definition.
 
        .PARAMETER TFSRootURL
            The root URL for TFS, e.g https://yourserver.yoursite.com/TFS
         
        .PARAMETER TFSCollection
            The name of the TFS collection to query.
         
        .PARAMETER TFSProject
            The name of the TFS project to query.
                          
        .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. Default: 'TFS'. This must exist in Influx!
 
        .EXAMPLE
            Send-TFSBuildMetric -Measure 'TestTFS' -Tags Name,Author -TFSRootURL https://localhost:8088/tfs -TFSCollection MyCollection -TFSProject MyProject
             
            Description
            -----------
            This command will submit the specified tags and Build metrics to a measure called 'TestTFS'.
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = 'TFSBuild',

        [String[]]
        $Tags = ('Definition', 'Id', 'Result'),

        [int]
        $Top = 100,

        [switch]
        $Latest,

        [Parameter(Mandatory = $true)]
        [string]
        $TFSRootURL,
        
        [Parameter(Mandatory = $true)]
        [string]
        $TFSCollection,
        
        [Parameter(Mandatory = $true)]
        [string]
        $TFSProject,
        
        [string]
        $Database = 'tfs',
        
        [string]
        $Server = 'http://localhost:8086'

    )

    $MetricParams = @{
        Measure       = $Measure
        Tags          = $Tags
        Top           = $Top
        Latest        = $Latest
        TFSRootURL    = $TFSRootURL
        TFSCollection = $TFSCollection
        TFSProject    = $TFSProject
    }

    $Metric = Get-TFSBuildMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Get-DatacenterMetric {
    <#
        .SYNOPSIS
            Returns VMWare Datacenter metrics as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            By default this cmdlet returns metrics for all Datacenters returned by Get-Datacenter.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER Tags
            An array of Datacenter tags to be included. Default: 'Name','ParentFolder'
 
        .PARAMETER Datacenter
            One or more Datacenters to be queried.
 
        .EXAMPLE
            Get-DatacenterMetric -Measure 'TestDatacenter' -Tags Name,NumCpuShares -Datacenter Test*
             
            Description
            -----------
            This command will return the specified tags and Datacenter metrics for a measure named 'TestDatacenter' for all Datacenters starting with 'Test'
    #>
  
    [cmdletbinding()]
    param(
        [String]
        $Measure = 'Datacenter',

        [String[]]
        $Tags = ('Name', 'ParentFolder'),

        [String[]]
        $Datacenter = '*'
    )

    Write-Verbose 'Getting Datacenters..'
    $Datacenters = Get-Datacenter $Datacenter

    if ($Datacenters) {
        
        foreach ($DC in $Datacenters) {
        
            $TagData = @{}
            ($DC | Select-Object $Tags).PSObject.Properties | ForEach-Object { 
                if ($_.Value) {
                    $TagData.Add($_.Name, $_.Value) 
                }
            }

            $VMs = $DC | Get-VM

            $Metrics = @{ VMs_Count = $VMs.count }

            If ($VMs.count -gt 0) {
                $Metrics.Add('VMs_MemoryGB_Total', ($VMs | Measure-Object MemoryGB -Sum).Sum)
                $Metrics.Add('VMs_NumCPU_Total', ($VMs | Measure-Object NumCPU -Sum).Sum)
            }
            
            $VMS | Group-Object PowerState | ForEach-Object { 
                $Metrics.Add("$($_.Name)_VMs_Count", $_.Count)
                If ($_.count -gt 0) {
                    $Metrics.Add("$($_.Name)_VMs_MemoryGB_Total", ($_.Group | Measure-Object MemoryGB -Sum).Sum) 
                    $Metrics.Add("$($_.Name)_VMs_NumCPU_Total", ($_.Group | Measure-Object NumCPU -Sum).Sum) 
                }
            }
            
            [pscustomobject]@{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = $Metrics
            }
        }
    }
    else {
        Write-Verbose 'No Datacenter data returned'
    }
}
Function Get-DatastoreClusterMetric {
    <#
        .SYNOPSIS
            Returns Datastore Cluster metrics as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            By default this cmdlet returns metrics for all Datastore Clusters returned by Get-DatastoreCluster.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER Tags
            An array of Datastore Cluster tags to be included. Default: 'Name'
 
        .PARAMETER DatastoreCluster
            One or more Datastore Clusters to be queried.
 
        .EXAMPLE
            Get-DatastoreClusterMetric -Measure 'TestDatastoreClusters' -Tags Name,Type -DatastoreCluster Test*
             
            Description
            -----------
            This command will return the specified tags and DatastoreCluster metrics for a measure called 'TestDatastoreClusters' for all DatastoreClusters starting with 'Test'.
    #>
  
    [cmdletbinding()]
    param(
        [String]
        $Measure = 'DatastoreCluster',

        [String[]]
        $Tags = 'Name',

        [String[]]
        $DatastoreCluster = '*'
    )

    Write-Verbose 'Getting DatastoreClusters..'
    $DatastoreClusters = Get-DatastoreCluster $DatastoreCluster

    if ($DatastoreClusters) {
        
        foreach ($DSCluster in $DatastoreClusters) {
        
            $TagData = @{}
            ($DSCluster | Select-Object $Tags).PSObject.Properties | ForEach-Object { 
                if ($_.Value) {
                    $TagData.Add($_.Name, $_.Value) 
                }
            }
            
            [pscustomobject]@{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = @{
                    CapacityGB       = $DSCluster.CapacityGB
                    FreeSpaceGB      = $DSCluster.FreeSpaceGB
                    UsedSpaceGB      = ($DSCluster.CapacityGB - $DSCluster.FreeSpaceGB)
                    UsedSpacePercent = (($DSCluster.CapacityGB - $DSCluster.FreeSpaceGB) / $DSCluster.CapacityGB * 100)
                }
            }
        }
    }
    else {
        Write-Verbose 'No DatastoreCluster data returned'
    }
}
Function Get-DatastoreMetric {
    <#
        .SYNOPSIS
            Returns Datastore metrics as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            By default this cmdlet returns metrics for all Datastores returned by Get-Datastore.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER Tags
            An array of datastore tags to be included. Default: 'Name','ParentFolder','Type'
 
        .PARAMETER Datastore
            One or more datastores to be queried.
 
        .EXAMPLE
            Send-DatastoreMetric -Measure 'TestDatastores' -Tags Name,Type -Datastore Test*
             
            Description
            -----------
            This command will submit the specified tags and datastore metrics to a measure called 'TestDatastores' for all datastores starting with 'Test'
    #>
  
    [cmdletbinding()]
    param(
        [String]
        $Measure = 'Datastore',

        [String[]]
        $Tags = ('Name', 'ParentFolder', 'Type'),

        [String[]]
        $Datastore = '*'
    )

    Write-Verbose 'Getting datastores..'
    $Datastores = Get-Datastore $Datastore

    if ($Datastores) {
        
        foreach ($DS in $Datastores) {
        
            $TagData = @{}
            ($DS | Select-Object $Tags).PSObject.Properties | ForEach-Object { 
                if ($_.Value) {
                    $TagData.Add($_.Name, $_.Value) 
                }
            }
            
            [pscustomobject]@{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = @{
                    CapacityGB  = $DS.CapacityGB
                    FreeSpaceGB = $DS.FreeSpaceGB
                    UsedSpaceGB = ($DS.CapacityGB - $DS.FreeSpaceGB)
                }
            }
        }
    }
    else {
        Write-Verbose 'No datastore data returned'
    }
}
Function Get-HostMetric {
    <#
        .SYNOPSIS
            Returns common ESX Host metrics as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            By default this cmdlet returns metrics for all ESX hosts returned by Get-VMHost.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER Tags
            An array of host tags to be included. Default: 'Name','Parent','State','PowerState','Version'
 
        .PARAMETER Hosts
            One or more hosts to be queried.
 
        .PARAMETER Stats
            Use this switch if you want to collect common host stats using Get-Stat.
     
        .EXAMPLE
            Get-HostMetric -Measure 'TestESXHosts' -Tags Name,Parent -Hosts TestHost*
             
            Description
            -----------
            This command will return the specified tag and common ESX host data for a measure called 'TestESXHosts' for all hosts starting with 'TestHost'
    #>
  
    [cmdletbinding()]
    param(
        [String]
        $Measure = 'ESXHost',

        [String[]]
        $Tags = ('Name', 'Parent', 'State', 'PowerState', 'Version'),

        [String[]]
        $Hosts = '*',

        [Switch]
        $Stats
    )

    Write-Verbose 'Getting hosts..'
    $VMHosts = Get-VMHost $Hosts

    if ($VMHosts) {

        if ($Stats) {
            Write-Verbose 'Getting host statistics..'
            $HostStats = $VMHosts | Get-Stat -MaxSamples 1 -Common | Where-Object {-not $_.Instance}
        }

        foreach ($VMHost in $VMHosts) {
        
            $TagData = @{}
            ($VMHost | Select-Object $Tags).PSObject.Properties | ForEach-Object { 
                if ($_.Value) {
                    $TagData.Add($_.Name, $_.Value) 
                }
            }

            $Metrics = @{
                CpuTotalMhz        = $VMHost.CpuTotalMhz
                CpuUsageMhz        = $VMHost.CpuUsageMhz
                CpuUsagePercent    = (($VMHost.CpuUsageMhz / $VMHost.CpuTotalMhz) * 100)
                MemoryTotalGB      = $VMHost.MemoryTotalGB
                MemoryUsageGB      = $VMHost.MemoryUsageGB
                MemoryUsagePercent = (($VMHost.MemoryUsageGB / $VMHost.MemoryTotalGB) * 100)
            }
            
            if ($HostStats) {
                $HostStats | Where-Object { $_.Entity.Name -eq $VMHost.Name } | ForEach-Object { $Metrics.Add($_.MetricId, $_.Value) }
            }

            [pscustomobject]@{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = $Metrics
            }
        }
    }
}
Function Get-ResourcePoolMetric {
    <#
        .SYNOPSIS
            Returns Resource Pool metrics as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            By default this cmdlet returns metrics for all Resource Pools returned by Get-ResourcePool.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER Tags
            An array of Resource Pool tags to be included. Default: 'Name','Parent'
 
        .PARAMETER ResourcePool
            One or more Resource Pools to be queried.
 
        .EXAMPLE
            Get-ResourcePoolMetric -Measure 'TestResources' -Tags Name,NumCpuShares -ResourcePool Test*
             
            Description
            -----------
            This command will return the specified tags and resource pool metrics for a measure called 'TestResources' for all resource pools starting with 'Test'
    #>
  
    [cmdletbinding()]
    param(
        [String]
        $Measure = 'ResourcePool',

        [String[]]
        $Tags = ('Name', 'Parent'),

        [String[]]
        $ResourcePool = '*'
    )

    Write-Verbose 'Getting resource pools..'
    $ResourcePools = Get-ResourcePool $ResourcePool

    if ($ResourcePools) {
        
        foreach ($RP in $ResourcePools) {
        
            $TagData = @{}
            ($RP | Select-Object $Tags).PSObject.Properties | ForEach-Object { 
                if ($_.Value) {
                    $TagData.Add($_.Name, $_.Value) 
                }
            }

            $VMs = $RP | Get-VM

            $Metrics = @{ VMs_Count = $VMs.count }

            If ($VMs.count -gt 0) {
                $Metrics.Add('VMs_MemoryGB_Total', ($VMs | Measure-Object MemoryGB -Sum).Sum)
                $Metrics.Add('VMs_NumCPU_Total', ($VMs | Measure-Object NumCPU -Sum).Sum)
            }
            
            $VMS | Group-Object PowerState | ForEach-Object { 
                $Metrics.Add("$($_.Name)_VMs_Count", $_.Count)
                If ($_.count -gt 0) {
                    $Metrics.Add("$($_.Name)_VMs_MemoryGB_Total", ($_.Group | Measure-Object MemoryGB -Sum).Sum) 
                    $Metrics.Add("$($_.Name)_VMs_NumCPU_Total", ($_.Group | Measure-Object NumCPU -Sum).Sum) 
                }
            }

            [pscustomobject]@{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = $Metrics
            }
        }
    }
}
Function Get-VMMetric {
    <#
        .SYNOPSIS
            Returns Virtual Machine metrics as a metric object which can then be transmitted to Influx.
 
        .DESCRIPTION
            By default this cmdlet returns metrics for all Virtual Machines returned by Get-VM.
 
        .PARAMETER Measure
            The name of the measure to be (ultimately) updated or created when this metric object is transmitted to Influx.
 
        .PARAMETER Tags
            An array of virtual machine tags to be included. Default: 'Name','Folder','ResourcePool','PowerState','Guest','VMHost'
 
        .PARAMETER VMs
            One or more Virtual Machines to be queried.
 
        .PARAMETER Stats
            Use to enable the collection of VM statistics via Get-Stat for each VM.
 
        .EXAMPLE
            Get-VMMetric -Measure 'TestVirtualMachines' -Tags Name,ResourcePool -Hosts TestVM*
             
            Description
            -----------
            This command will return the specified tag and common VM host data for a measure called 'TestVirtualMachines' for all VMs starting with 'TestVM'
    #>
  
    [cmdletbinding()]
    param(
        [string]
        $Measure = 'VirtualMachine',

        [string[]]
        $Tags = ('Name', 'Folder', 'ResourcePool', 'PowerState', 'Guest', 'VMHost'),

        [string[]]
        $VMs = '*',

        [switch]
        $Stats
    )

    Write-Verbose 'Getting VMs..'
    $VMServers = Get-VM $VMs

    if ($VMServers) {
        
        if ($Stats) {
            Write-Verbose 'Getting VM statistics..'
            $VMStats = $VMServers | Get-Stat -MaxSamples 1 -Common | Where-Object {-not $_.Instance}
        }

        foreach ($VM in $VMServers) {
        
            $TagData = @{}
            ($VM | Select-Object $Tags).PSObject.Properties | ForEach-Object {     
                if ($_.Value) {
                    $TagData.Add($_.Name, $_.Value) 
                }
            }

            $Metrics = @{
                PowerState           = [int]$VM.PowerState
                GuestHeartbeatStatus = [int]$VM.ExtensionData.Summary.QuickStats.GuestHeartbeatStatus
            }

            $QuickStats = $VM.ExtensionData.Summary.QuickStats | Select-Object OverallCpuUsage, GuestMemoryUsage, HostMemoryUsage, UptimeSeconds
            
            $QuickStats.PSObject.Properties | ForEach-Object {     
                if ($_.Value) {
                    $Metrics.Add($_.Name, $_.Value) 
                }
            }

            if ($VMStats) {
                $VMStats | Where-Object { $_.Entity.Name -eq $VM.Name } | ForEach-Object { $Metrics.Add($_.MetricId, $_.Value) }
            }

            [pscustomobject]@{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = $Metrics
            }
        }
    }
}
Function Send-DatacenterMetric {
    <#
        .SYNOPSIS
            Sends Datacenter metrics to Influx.
 
        .DESCRIPTION
            By default this cmdlet sends metrics for all Datacenter returned by Get-Datacenter.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            An array of Datacenter tags to be included. Default: 'Name','ParentFolder'
 
        .PARAMETER Datacenter
            One or more Datacenters to be queried.
 
        .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. Default: 'vmware'. This must exist in Influx!
 
        .EXAMPLE
            Send-DatacenterMetric -Measure 'TestDatacenter' -Tags Name,NumCpuShares -Datacenter Test*
             
            Description
            -----------
            This command will submit the specified tags and Datacenter metrics to a measure called 'TestDatacenter' for all Datacenters starting with 'Test'
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = 'Datacenter',

        [String[]]
        $Tags = ('Name', 'ParentFolder'),

        [String[]]
        $Datacenter = '*',

        [string]
        $Database = 'vmware',
        
        [string]
        $Server = 'http://localhost:8086'
    )

    $MetricParams = @{
        Measure    = $Measure
        Tags       = $Tags
        Datacenter = $Datacenter
    }

    $Metric = Get-DatacenterMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Send-DatastoreClusterMetric {
    <#
        .SYNOPSIS
            Sends Datastore Cluster metrics to Influx.
 
        .DESCRIPTION
            By default this cmdlet sends metrics for all Datastore Clusters returned by Get-DatastoreCluster.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            An array of Datastore Cluster tags to be included. Default: 'Name'
 
        .PARAMETER DatastoreCluster
            One or more Datastore Clusters to be queried.
 
        .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. Default: 'vmware'. This must exist in Influx!
 
        .EXAMPLE
            Send-DatastoreClusterMetric -Measure 'TestDatastoreClusters' -Tags Name,Type -DatastoreCluster Test*
             
            Description
            -----------
            This command will submit the specified tags and DatastoreCluster metrics to a measure called 'TestDatastoreClusters' for all DatastoreClusters starting with 'Test'
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = 'DatastoreCluster',

        [String[]]
        $Tags = 'Name',

        [String[]]
        $DatastoreCluster = '*',

        [string]
        $Database = 'vmware',
        
        [string]
        $Server = 'http://localhost:8086'
    )

    $MetricParams = @{
        Measure          = $Measure
        Tags             = $Tags
        DatastoreCluster = $DatastoreCluster
    }

    $Metric = Get-DatastoreClusterMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Send-DatastoreMetric {
    <#
        .SYNOPSIS
            Sends Datastore metrics to Influx.
 
        .DESCRIPTION
            By default this cmdlet sends metrics for all Datastores returned by Get-Datastore.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            An array of datastore tags to be included. Default: 'Name','ParentFolder','Type'
 
        .PARAMETER Datastore
            One or more datastores to be queried.
 
        .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. Default: 'vmware'. This must exist in Influx!
 
        .EXAMPLE
            Send-DatastoreMetric -Measure 'TestDatastores' -Tags Name,Type -Datastore Test*
             
            Description
            -----------
            This command will submit the specified tags and datastore metrics to a measure called 'TestDatastores' for all datastores starting with 'Test'
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = 'Datastore',

        [String[]]
        $Tags = ('Name', 'ParentFolder', 'Type'),

        [String[]]
        $Datastore = '*',

        [string]
        $Database = 'vmware',
        
        [string]
        $Server = 'http://localhost:8086'
    )

    $MetricParams = @{
        Measure   = $Measure
        Tags      = $Tags
        Datastore = $Datastore
    }

    $Metric = Get-DatastoreMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Send-HostMetric {
    <#
        .SYNOPSIS
            Sends common ESX Host metrics to Influx.
 
        .DESCRIPTION
            By default this cmdlet sends metrics for all ESX hosts returned by Get-VMHost.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            An array of host tags to be included. Default: 'Name','Parent','State','PowerState','Version'
 
        .PARAMETER Hosts
            One or more hosts to be queried.
 
        .PARAMETER Stats
            Use this switch if you want to collect common host stats using Get-Stat.
         
        .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. Default: 'vmware'. This must exist in Influx!
 
        .EXAMPLE
            Send-HostMetric -Measure 'TestESXHosts' -Tags Name,Parent -Hosts TestHost*
             
            Description
            -----------
            This command will submit the specified tag and common ESX host data to a measure called 'TestESXHosts' for all hosts starting with 'TestHost'
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = 'ESXHost',

        [String[]]
        $Tags = ('Name', 'Parent', 'State', 'PowerState', 'Version'),

        [String[]]
        $Hosts = '*',

        [Switch]
        $Stats,

        [string]
        $Database = 'vmware',
        
        [string]
        $Server = 'http://localhost:8086'
    )

    $MetricParams = @{
        Measure = $Measure
        Tags    = $Tags
        Hosts   = $Hosts
        Stats   = $Stats
    }

    $Metric = Get-HostMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Send-ResourcePoolMetric {
    <#
        .SYNOPSIS
            Sends Resource Pool metrics to Influx.
 
        .DESCRIPTION
            By default this cmdlet sends metrics for all Resource Pool returned by Get-ResourcePool.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            An array of Resource Pool tags to be included. Default: 'Name','Parent'
 
        .PARAMETER ResourcePool
            One or more Resource Pools to be queried.
 
        .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. Default: 'vmware'. This must exist in Influx!
 
        .EXAMPLE
            Send-ResourcePoolMetric -Measure 'TestResources' -Tags Name,NumCpuShares -ResourcePool Test*
             
            Description
            -----------
            This command will submit the specified tags and resource pool metrics to a measure called 'TestResources' for all resource pools starting with 'Test'
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [String]
        $Measure = 'ResourcePool',

        [String[]]
        $Tags = ('Name', 'Parent'),

        [String[]]
        $ResourcePool = '*',

        [string]
        $Database = 'vmware',
        
        [string]
        $Server = 'http://localhost:8086'
    )

    $MetricParams = @{
        Measure      = $Measure
        Tags         = $Tags
        ResourcePool = $ResourcePool
    }

    $Metric = Get-ResourcePoolMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function Send-VMMetric {
    <#
        .SYNOPSIS
            Sends Virtual Machine metrics to Influx.
 
        .DESCRIPTION
            By default this cmdlet sends metrics for all Virtual Machines returned by Get-VM.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER Tags
            An array of virtual machine tags to be included. Default: 'Name','Folder','ResourcePool','PowerState','Guest','VMHost'
 
        .PARAMETER VMs
            One or more Virtual Machines to be queried.
 
        .PARAMETER Stats
            Use to enable the collection of VM statistics via Get-Stat for each VM.
 
        .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. Default: 'vmware'. This must exist in Influx!
 
        .EXAMPLE
            Send-VMMetric -Measure 'TestVirtualMachines' -Tags Name,ResourcePool -Hosts TestVM*
             
            Description
            -----------
            This command will submit the specified tag and common VM host data to a measure called 'TestVirtualMachines' for all VMs starting with 'TestVM'
    #>
  
    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param(
        [string]
        $Measure = 'VirtualMachine',

        [string[]]
        $Tags = ('Name', 'Folder', 'ResourcePool', 'PowerState', 'Guest', 'VMHost'),

        [string[]]
        $VMs = '*',

        [switch]
        $Stats,

        [string]
        $Database = 'vmware',
        
        [string]
        $Server = 'http://localhost:8086'
    )

    $MetricParams = @{
        Measure = $Measure
        Tags    = $Tags
        VMs     = $VMs
        Stats   = $Stats
    }

    $Metric = Get-VMMetric @MetricParams
    
    if ($Metric.Measure) {

        if ($PSCmdlet.ShouldProcess($Metric.Measure)) {
            $Metric | Write-Influx -Database $Database -Server $Server
        }
    }
}
Function ConvertTo-InfluxLineString {
    <#
        .SYNOPSIS
            Converts metric objects or data to the Influx line format.
 
        .DESCRIPTION
            Use to convert some metrics in to the Influx line format for later consumption in to Influx such as via the Telegraf exec plugin.
 
        .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 ExcludeEmptyMetric
            Switch: Use to exclude null or empty metric values from being processed. 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.
 
        .EXAMPLE
            ConvertTo-InfluxLineString -Measure WebServer -Tags @{Server='Host01'} -Metrics @{CPU=100; Memory=50}
             
            Description
            -----------
            This command will output the provided tag and metric data for a measure called 'WebServer' as strings in the Influx line format.
    #>

    [cmdletbinding(SupportsShouldProcess = $true, ConfirmImpact = 'Medium')]
    param (
        [Parameter(ParameterSetName = 'MetricObject', Mandatory = $true, ValueFromPipeline = $True, Position = 0)]
        [PSTypeName('Metric')]
        [PSObject[]]
        $InputObject,

        [Parameter(ParameterSetName = 'Measure', Mandatory = $true, Position = 0)]
        [string]
        $Measure,

        [Parameter(ParameterSetName = 'Measure')]
        [hashtable]
        $Tags,
        
        [Parameter(ParameterSetName = 'Measure', Mandatory = $true)]
        [hashtable]
        $Metrics,

        [Parameter(ParameterSetName = 'Measure')]
        [datetime]
        $TimeStamp,

        [switch]
        $ExcludeEmptyMetric
    )
    Begin {
    }
    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) {
                $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 | Sort-Object) -Join ',' 
            }
            
            #No existance check performed since the parameter is mandatory
            $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 {
                    $MetricValue = $MetricObject.Metrics[$Metric] | Format-InfluxFieldValue
                    #Write output
                    "$($Metric | Out-InfluxEscapeString)=$($MetricValue)"
                }
                
            }
            $MetricData = $MetricData -Join ','

            $Body = "$($MetricObject.Measure | Out-InfluxEscapeString -StringType Measurement)"+ $(if($TagData) {","}) + $TagData + " " + $MetricData + $(if($timeStampNanoSecs) {" "}) + $timeStampNanoSecs 

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

                if ($PSCmdlet.ShouldProcess($Body)) {
                    Return $Body
                }            
            }
        }
        
    }
    End { }
}

Function ConvertTo-Metric {
    <#
        .SYNOPSIS
            Converts the specified properties of an object to a metric object, which can then be easily transmitted to Influx.
 
        .DESCRIPTION
            Use to convert any PowerShell object in to a Metric object with specified Measure name, Metrics and Tags. The metrics
            are one or more named properties of the object. The (optional) Tags can be one or more named properties of the object
            and/or a provided hashtable of custom tags.
 
        .PARAMETER InputObject
            The object that you want to convert to a metric object. Can be provided via the pipeline.
 
        .PARAMETER Measure
            The name of the measure to be updated or created.
 
        .PARAMETER MetricProperty
            One or more strings which match property names of the Input Object that you want to convert to Metrics.
 
        .PARAMETER TagProperty
            Optional: One or more strings which match property names of the Input Object, that you want to use as Tag data.
 
        .PARAMETER TimeProperty
            Optional: A string that matches a property name of the Input Object, that you want to use as the TimeStamp data.
 
        .PARAMETER Tags
            Optional: A hashtable of custom tag names and values.
 
        .EXAMPLE
            Get-Process | ConvertTo-Metric -Measure Processes -MetricProperty CPU -TagProperty Name,ID -Tags @{Host = $Env:ComputerName}
             
            Description
            -----------
            This command will convert the specified object properties in to metrics and tags for a measure called 'Processes'.
    #>
  
    [cmdletbinding()]
    param(
        [Parameter(ValueFromPipeline = $True, Position = 0)]
        [Object]
        $InputObject,

        [Parameter(Mandatory = $true)]
        [String]
        $Measure,

        [Parameter(Mandatory = $true)]
        [String[]]
        $MetricProperty,

        [String[]]
        $TagProperty,

        [String]
        $TimeProperty,

        [hashtable]
        $Tags
    )
    Process {

        ForEach ($ItemObject in $InputObject) {

            $Metrics = @{}

            ForEach ($Metric in $MetricProperty) {
                If ($Metric -in $ItemObject.PSobject.Properties.Name) {
                    $Metrics.Add($Metric, $ItemObject.$Metric)
                }
                Else {
                    Write-Error "$Metric is not a valid property for InputObject."
                }            
            }
            $TagData = @{}

            ForEach ($Tag in $TagProperty) {
                If ($Tag -in $ItemObject.PSobject.Properties.Name) {
                    $TagData.Add($Tag, $ItemObject.$Tag)
                }
                Else {
                    Write-Error "$Tag is not a valid property for InputObject."
                }            
            }

            If ($Tags.Count -ne 0) { $TagData += $Tags }

            $Result = @{
                PSTypeName = 'Metric'
                Measure    = $Measure
                Tags       = $TagData
                Metrics    = $Metrics
            }

            if ($ItemObject.$TimeProperty) {
                $Result.add('TimeStamp', $(Get-Date $ItemObject.$TimeProperty))
            }

            [PSCustomObject]$Result
        }
    }
}
Function ConvertTo-StatsDString {
    <#
        .SYNOPSIS
            Converts a metric object to a StatsD format string which could be used with Write-StatsD.
 
        .DESCRIPTION
            This is the format a StatsD listener expects for writing metrics.
 
        .PARAMETER InputObject
            The metric object to be converted.
 
        .PARAMETER Type
            The type of StatsD metric. Default: g (gauge: will record whatever exact value is included with each metric provided).
            See Statsd documentation for explanations of the different metric types accepted.
 
        .EXAMPLE
            Get-HostMetric -Hosts somehost1 -Tags Name,PowerState | ConvertTo-StatsDString
             
            Result
            -------------------
            CpuUsageMhz,Name=somehost1,PowerState=PoweredOn:305|g
            MemoryUsageGB,Name=somehost1,PowerState=PoweredOn:17.0029296875|g
    #>

    [cmdletbinding()]
    [OutputType([String])]
    [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSReviewUnusedParameter', 'Type', Justification = 'Used inside the ForEach-Object closure below; PSScriptAnalyzer does not see usage inside nested scriptblocks.')]
    Param(
        [Parameter(ValueFromPipeline = $True, Position = 0)]
        [PSTypeName('Metric')]
        $InputObject,

        [string]
        $Type = 'g'
    )
    Process {
        $InputObject | ForEach-Object {
            
            $Tags = @()
            ForEach ($Tag in $_.Tags.GetEnumerator() | Sort-Object Key) {
                $Tags += "$($Tag.Key)=$($Tag.Value)"
            }
            
            $TagData = ',' + ($Tags -Join ',')
            
            ForEach ($Metric in $_.Metrics.GetEnumerator() | Sort-Object Key) {
                "$($_.Measure).$($Metric.Key)$TagData`:$($Metric.Value)|$Type"
            }
        }
    }
}
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 {
                        "$($Metric | Out-InfluxEscapeString)=$($MetricObject.Metrics[$Metric] | Format-InfluxFieldValue)"
                    }
                }
                $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 {
                        $MetricValue = $MetricObject.Metrics[$Metric] | Format-InfluxFieldValue

                        "$($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
        }
    }
}

Function Write-InfluxUDP {
    <#
        .SYNOPSIS
            Send metrics to the Influx UDP listener (UDP must be enabled in influxdb.conf) for writing to Influx.
 
        .DESCRIPTION
            Use to write data in to an Influx database via UDP 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 IP
            IP address for InfluxDB UDP listener.
         
        .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 Port
            Port for InfluxDB UDP listener.
 
        .EXAMPLE
            Write-InfluxUDP -Measure WebServer -Tags @{Server='Host01'} -Metrics @{CPU=100; Memory=50} -IP 1.2.3.4 -Port 8089
             
            Description
            -----------
            This command will submit the provided tag and metric data for a measure called 'WebServer' via the endpoint 'udp://1.2.3.4:8089'
    #>
  
    [cmdletbinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    param(
        [Parameter(ParameterSetName = 'MetricObject', Mandatory = $true, ValueFromPipeline = $True, Position = 0)]
        [PSTypeName('Metric')]
        [PSObject[]]
        $InputObject,
        
        [Parameter(ParameterSetName = 'Measure', Mandatory = $true, Position = 0)]
        [string]
        $Measure,

        [Parameter(ParameterSetName = 'Measure')]
        [hashtable]
        $Tags,
        
        [Parameter(ParameterSetName = 'Measure', Mandatory = $true)]
        [hashtable]
        $Metrics,

        [Parameter(ParameterSetName = 'Measure')]
        [datetime]
        $TimeStamp,
        
        [ipaddress]
        $IP = '127.0.0.1',
  
        [int]
        $Port = 8089,

        [switch]
        $ExcludeEmptyMetric
    )
    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"
            }
        
            $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 {
                    $MetricValue = $MetricObject.Metrics[$Metric] | Format-InfluxFieldValue

                    "$($MetricObject.Measure | Out-InfluxEscapeString)$TagData $($Metric | Out-InfluxEscapeString)=$MetricValue $timeStampNanoSecs"
                }
            }
    
            if ($Body) {
                $Body = $Body -Join "`n"
            
                if ($PSCmdlet.ShouldProcess("$($IP):$Port", "$Body")) {
                    $Body | Invoke-UDPSendMethod -IP $IP -Port $Port
                }
            }
        }
    }
}

Function Write-StatsD {
    <#
        .SYNOPSIS
            Send metrics to a statsd server via UDP for writing to Influx.
 
        .DESCRIPTION
            PowerShell cmdlet to send metric data to a statsd server. Unless server or port is passed, uses the default 127.0.0.1 and port of 8125.
     
        .PARAMETER InputObject
            A metric object (generated by one of the Get-*Metric cmdlets from this module) which can be provided as pipeline input
            and will be automatically converted to StatsD strings.
 
        .PARAMETER Data
            Metric data to send to statsd. If string is not enclosed in quotes (single or double), the pipe character needs to be escaped.
     
        .PARAMETER IP
            IP address for statsd server
     
        .PARAMETER Port
            Port that statsd server is listening to
     
        .EXAMPLE
            Write-StatsD 'my_metric:123|g'
 
            This will write a value to a gauge metric named my_metric.
 
        .EXAMPLE
            Write-StatsD 'my_metric:321|g' -ip 10.0.0.10 -port 8180
 
            This will write a value to a gauge metric named my_metric via the specified IP and port.
 
        .EXAMPLE
            'my_metric:1|c' | Write-StatsD
 
            This will write a value to a counter metric, using the piepline as input for the cmdlet.
    #>

    [cmdletbinding(SupportsShouldProcess, ConfirmImpact = 'Medium')]
    param(
        [Parameter(ParameterSetName = 'MetricObject', Mandatory = $True, ValueFromPipeline = $True, Position = 0)]
        [PSTypeName('Metric')]
        [PSObject[]]
        $InputObject,
        
        [parameter(ParameterSetName = 'StatsDString', Mandatory = $True, ValueFromPipeline = $True, Position = 0)]
        [string[]]
        $Data,

        [ipaddress]
        $IP = '127.0.0.1',
  
        [int]
        $Port = 8125
    )
    Process {
        if ($InputObject) {
            $Data = $InputObject | ConvertTo-StatsDString
        }

        foreach ($Item in $Data) {

            if ($PSCmdlet.ShouldProcess("$($IP):$Port", "$($MyInvocation.MyCommand) -Data $Item")) {
                $Item | Invoke-UDPSendMethod -IP $IP -Port $Port
            }
        }
    } 
}
Function ConvertTo-UnixTimeMillisecond {
    <#
        .SYNOPSIS
            Converts a datetime string to a Unix time code in milliseconds.
 
        .DESCRIPTION
            This is the datetime format Influx expects by default for writing datetime fields.
 
        .PARAMETER Date
            The date/time to be converted.
 
        .EXAMPLE
            '01-01-2017 12:34:22.12' | ConvertTo-UnixTimeMillisecond
             
            Result
            -----------
            1483274062120
    #>
      
    [cmdletbinding()]
    [OutputType([double])]
    Param(
        [parameter(ValueFromPipeline)]
        $Date
    )
    Process {
        (New-TimeSpan -Start (Get-Date -Date '01/01/1970') -End $Date).TotalMilliseconds
    }
}
Function ConvertTo-UnixTimeNanosecond {
    <#
        .SYNOPSIS
            Converts a datetime object to a Unix time code in nanoseconds.
 
        .DESCRIPTION
            This is the datetime format Influx expects for writing the (optional) timestamp field.
 
        .PARAMETER Date
            The date/time to be converted.
 
        .EXAMPLE
            '01-01-2017 12:34:22.12' | ConvertTo-UnixTimeNanosecond
             
            Result
            -------------------
            1483274062120000000
    #>
      
    [cmdletbinding()]
    [OutputType([long])]
    Param(
        [parameter(ValueFromPipeline)]
        [datetime]
        $Date
    )
    Process {
        [long]((New-TimeSpan -Start (Get-Date -Date '1970-01-01') -End (($Date).ToUniversalTime())).TotalSeconds * 1E9)
    }
}
Function Format-InfluxFieldValue {
    <#
        .SYNOPSIS
            Formats a raw metric value as an Influx Line Protocol field value.
 
        .DESCRIPTION
            Used by Write-Influx, Write-InfluxUDP and ConvertTo-InfluxLineString to format a metric value ready for
            inclusion in a line protocol string: numeric and boolean values are passed through unescaped, [datetime]
            values are converted to a Unix nanosecond integer field (Influx has no native date/time field type), and
            everything else is quoted and escaped as a text field value.
 
        .PARAMETER Value
            The raw metric value to format.
 
        .EXAMPLE
            (Get-Date) | Format-InfluxFieldValue
 
        .EXAMPLE
            100 | Format-InfluxFieldValue
    #>

    [cmdletbinding()]
    [OutputType([string])]
    param(
        [parameter(ValueFromPipeline)]
        $Value
    )
    process {
        if ($Value -is [datetime]) {
            # Influx has no date/time field type, so store it as nanoseconds since the Unix epoch (the same
            # precision Influx itself uses for the line's timestamp) rather than a culture-dependent ToString().
            "$($Value | ConvertTo-UnixTimeNanosecond)i"
        }
        elseif ($Value -isnot [ValueType]) {
            if ($null -ne $Value -and $Value -isnot [string] -and $Value.GetType().GetMethod('ToString', [Type]::EmptyTypes).DeclaringType -eq [object]) {
                Write-Warning "Metric value of type [$($Value.GetType().FullName)] has no meaningful ToString() representation and will be written as the literal string '$Value'. Convert it to a suitable value (e.g. a specific property) before passing it to this function."
            }
            '"' + ($Value | Out-InfluxEscapeString -StringType FieldTextValue) + '"'
        }
        else {
            $Value
        }
    }
}

Function Invoke-UDPSendMethod {
    <#
        .SYNOPSIS
            Send data to a UDP listener.
 
        .DESCRIPTION
            Uses the System.Net.IPEndPoint and System.Net.Sockets.UdpClient .NET methods to transmit data via UDP.
 
        .PARAMETER Data
            String data to be transmitted via UDP.
 
        .PARAMETER IP
            IP address for UDP listener. 127.0.0.1 by default.
     
        .PARAMETER Port
            Port for UDP listener. 8089 by default.
 
        .EXAMPLE
            'Some Data' | Invoke-UDPSendMethod -IP 1.2.3.4 -Port 1234
             
            Description
            -----------
            This command will transmit the data provided via the pipeline to the specified IP address and port via UDP.
    #>

    [cmdletbinding(SupportsShouldProcess, ConfirmImpact='Medium')]
    param(
        [parameter(Mandatory,ValueFromPipeline)]
        [string[]]
        $Data,
    
        [ipaddress]
        $IP = '127.0.0.1',

        [int]
        $Port = 8089
    )
    Begin {
        $Endpoint  = New-Object System.Net.IPEndPoint($IP, $Port)
        $UDPClient = New-Object System.Net.Sockets.UdpClient
    }
    Process {
        ForEach ($DataItem in $Data) {
            $EncodedData = [System.Text.Encoding]::ASCII.GetBytes($DataItem)
    
            if ($PSCmdlet.ShouldProcess("$($IP):$Port","$DataItem")) {
                $BytesSent = $UDPClient.Send($EncodedData, $EncodedData.length, $Endpoint)
                Write-Verbose "Transmitted $BytesSent Bytes."
            }
        }        
    }    
    End {
        $UDPClient.Close()
    }
}
Function Out-InfluxEscapeString { 
    <#
        .SYNOPSIS
            Escapes the Influx REST API illegal characters using '\', several options are available based on the influx object to escape (measurement, field name, field value, etc)
 
        .DESCRIPTION
            Used in the Write-Influx function to escape measurement, tag and metric names and values before submitting them to the REST API.
 
        .PARAMETER String
            The string to be escaped.
 
        .PARAMETER StringType
            The influx object to be escaped: Measurement / FieldTextValue / Other. if not specified defaults to "Other"
 
        .EXAMPLE
            'Some ,string=' | Out-InfluxEscapeString
             
            Result
            -----------
            Some\ \,string\=
    #>

    [cmdletbinding()]
    [OutputType([string])]
    param(
        [parameter(ValueFromPipeline)]
        [string]
        $String,
        [parameter()]
        [ValidateSet("Measurement","FieldTextValue","Other")]
        [string]
        $StringType
    )
    process {
        # Backslashes are not escaped for Measurement/Other: InfluxDB line protocol does not treat '\' as
        # a reserved character outside of quoted field values, and escaping it here would corrupt values
        # such as Windows paths (e.g. 'C:\' becoming 'C:\\').
        if ($StringType -ne 'FieldTextValue' -and $String -match '\\$') {
            Write-Warning "'$String' ends with a backslash. InfluxDB line protocol cannot parse a measurement, tag key/value or field key ending in a backslash and will reject the write; consider removing or replacing the trailing backslash."
        }

        Switch ($StringType) {
            # Measurement names only need whitespace and commas escaped; there's no key=value structure to protect.
            "Measurement" { $String -Replace '(\s|,)', '\$1' }
            # Field text values are wrapped in "..." so the characters that would break out of the quotes need escaping.
            "FieldTextValue" { $String -Replace '("|\\)', '\$1' }
            # Tag keys/values and field keys are part of key=value,key=value pairs, so = and , are structural and must be escaped, along with whitespace.
            "Other" { $String -Replace '(\s|=|,)', '\$1' }
            # No -StringType specified: treat the same as "Other".
            default { $String -Replace '(\s|=|,)', '\$1' }
        }
    }
}