PSCoreApplicationInsights.psm1

function New-ApplicationInsightsClient {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, HelpMessage = "The Application Insights Instrumentation Key that is used to send the messages to the correct Application Insights Instance.")]
        [ValidateNotNullOrEmpty()]
        [Guid]
        $InstrumentationKey
    )

    $Client = [Microsoft.ApplicationInsights.TelemetryClient]::new()
    $Client.InstrumentationKey = $InstrumentationKey

    $defaultUserInformation = @{
        AuthenticatedUserId = whoami
        UserAgent           = ("PS $($psversiontable.PSEdition) $($psversiontable.PSVersion)")
    }

    $defaultDeviceInformation = @{
        OperatingSystem = $psversiontable.OS

    }

    $client = Set-ApplicationInsightsClientInformation -UserInformation $defaultUserInformation -DeviceInformation $defaultDeviceInformation -Client $Client

    return $Client
}

Export-ModuleMember -Function New-ApplicationInsightsClient
function Confirm-ApplicationInsightsClient {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Microsoft.ApplicationInsights.TelemetryClient]
        $Client
    )

    [bool] $isValid = $true
    Write-Verbose ("Checking if the Application Insights Client is valid...")

    if ([string]::IsNullOrWhiteSpace($client.InstrumentationKey)) {
        Write-Verbose ("The Instrumentation Key is not set.")
        $isValid = $false
    }

    if ($client.Isenabled() -eq $false) {
        Write-Verbose ("The Application Insights Client is not enabled.")
        $isValid = $false
    }

    if ($isValid -eq $true) {
        Write-Verbose ("The Application Insights Client is valid.")
    }
    else {
        throw ("The Application Insights Client is not valid.")
    }
}

function Set-ApplicationInsightsClientInformation {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Microsoft.ApplicationInsights.TelemetryClient]
        $Client,

        [Parameter(Mandatory = $false)]
        [hashtable]
        $UserInformation,

        [Parameter(Mandatory = $false)]
        [hashtable]
        $DeviceInformation
    )
    begin {
        if (-not $null -eq $UserInformation) {
            Write-Verbose ("Received 'User' properties to set in the client")
        }

        if (-not $null -eq $DeviceInformation) {
            Write-Verbose ("Received 'Device' properties to set in the client")
        }
    }

    process {
        if (-not $null -eq $UserInformation) {

            foreach ($property in $Client.Context.User.psobject.Properties.name) {
                Write-Verbose ("Checking property '$($property)' in supplied hashtable")
                if (-not [string]::IsNullOrWhiteSpace($UserInformation[$property])) {
                    Write-Verbose ("Found property '$($property)' with a value. Changing value from '$($Client.Context.User.$property)' to '$($UserInformation[$property])'")

                    $Client.Context.User.$property = $UserInformation[$property]
                }
            }
        }

        if (-not $null -eq $DeviceInformation) {

            foreach ($property in $Client.Context.Device.psobject.Properties.name) {
                Write-Verbose ("Checking property '$($property)' in supplied hashtable")
                if (-not [string]::IsNullOrWhiteSpace($DeviceInformation[$property])) {
                    Write-Verbose ("Found property '$($property)' with a value. Changing value from '$($Client.Context.Device.$property)' to '$($DeviceInformation[$property])'")

                    $Client.Context.Device.$property = $DeviceInformation[$property]
                }
            }
        }
    }

    end {
        return $Client
    }

}

Export-ModuleMember -Function Set-ApplicationInsightsClientInformation

function Write-ApplicationInsightsTrace {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Microsoft.ApplicationInsights.TelemetryClient]
        $Client,

        [Parameter(Mandatory = $true)]
        [string]
        $Message,

        [Parameter(Mandatory = $false)]
        [validateSet('Information', 'Verbose', 'Warning', 'Error', 'Critical')]
        [string]
        $SeverityLevel = "information",

        [Parameter(Mandatory = $false, HelpMessage = "This is a dictionary<string, string> with additional information that will be added as 'customDimensions' in Application Insights")]
        [System.Collections.Generic.Dictionary[string, string]]
        $properties
    )
    BEGIN {
        Write-Verbose ("Received '$($SeverityLevel)' severity level for the message '$($Message)'")

        if ($properties.Count -ge 1) {
            Write-Verbose ("Received '$($properties.Count)' properties to add to the message.")
        }

        Confirm-ApplicationInsightsClient $client
    }
    PROCESS {
        if ($properties.Count -ge 1) {
            $Client.TrackTrace($Message, [Microsoft.ApplicationInsights.DataContracts.SeverityLevel]::$($SeverityLevel), $properties)
        }
        else {
            $Client.TrackTrace($Message, [Microsoft.ApplicationInsights.DataContracts.SeverityLevel]::$($SeverityLevel))
        }

    }
    END {
        $Client.Flush()
    }
}

Export-ModuleMember -Function Write-ApplicationInsightsTrace

function Write-ApplicationInsightsMetric {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [Microsoft.ApplicationInsights.TelemetryClient]
        $Client,

        [Parameter(Mandatory = $true)]
        [string]
        $Name,

        [Parameter(Mandatory = $true)]
        [Double]
        $Metric,

        [Parameter(Mandatory = $false, HelpMessage = "This is a dictionary<string, string> with additional information that will be added as 'customDimensions' in Application Insights")]
        [System.Collections.Generic.Dictionary[string, string]]
        $properties
    )
    BEGIN {

        if ($properties.Count -ge 1) {
            Write-Verbose ("Received '$($properties.Count)' properties to add to the message.")
        }
    }
    PROCESS {
        if ($properties.Count -ge 1) {
            $client.TrackMetric($name, $Metric, $properties)
        }
        else {
            $client.TrackMetric($name, $Metric)
        }

    }
    END {
        $Client.Flush()
    }
}

Export-ModuleMember -Function Write-ApplicationInsightsMetric
function Write-ApplicationInsightsException {
    [CmdletBinding(DefaultParameterSetName = "Exception")]
    param (
        [Parameter(Mandatory = $true)]
        [Microsoft.ApplicationInsights.TelemetryClient]
        $Client,

        [Parameter(Mandatory = $true, ParameterSetName = "Exception")]
        [System.Exception]
        $Exception,

        [Parameter(Mandatory = $true, ParameterSetName = "StringException")]
        [String]
        $ExceptionString,

        [Parameter(Mandatory = $false, HelpMessage = "This is a dictionary<string, double> with additional information that will be added as 'customMeasurements' in Application Insights")]
        [System.Collections.Generic.Dictionary[string, double]]
        $Metrics = [System.Collections.Generic.Dictionary[string, double]]::new(),

        [Parameter(Mandatory = $false, HelpMessage = "This is a dictionary<string, string> with additional information that will be added as 'customDimensions' in Application Insights")]
        [System.Collections.Generic.Dictionary[string, string]]
        $properties = [System.Collections.Generic.Dictionary[string, string]]::new()
    )
    BEGIN {
        Write-Verbose ("Running in Parameterset '$($PSCmdlet.ParameterSetName)'")

        if ($PSCmdlet.ParameterSetName -eq "StringException") {
            $Exception = [System.Exception]::new($ExceptionString)
        }
    }
    PROCESS {
        $client.TrackException($Exception, $properties, $Metrics)

        $client.TrackExce
    }
    END {
        $Client.Flush()
    }
}

Export-ModuleMember -Function Write-ApplicationInsightsException

function Write-ApplicationInsightsRequest {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [String]
        $Name,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [System.DateTimeOffset]
        $StartTime,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Timespan]
        $Duration,

        [Parameter(Mandatory = $false)]
        [ValidateNotNullOrEmpty()]
        [String]
        $responseCode = "200",

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [Bool]
        $success,

        [Parameter(Mandatory = $false, HelpMessage = "This is a dictionary<string, string> with additional information that will be added as 'customProperties' in Application Insights")]
        [System.Collections.Generic.Dictionary[string, string]]
        $properties = [System.Collections.Generic.Dictionary[string, string]]::new(),

        [Parameter(Mandatory = $false, HelpMessage = "This is the URL that will be added as 'url' in Application Insights")]
        [ValidateNotNullOrEmpty()]
        [string]
        $url
    )
    BEGIN {
        Write-Verbose ("Received '$($Name)' name for the request")
        Write-Verbose ("Received '$($StartTime)' start time for the request")
        Write-Verbose ("Received '$($Duration)' duration for the request")
        Write-Verbose ("Received '$($responseCode)' response code for the request")
        Write-Verbose ("Received '$($success)' success for the request")
    }
    PROCESS {

        $requestTelemetry = [Microsoft.ApplicationInsights.DataContracts.RequestTelemetry]::new()

        $requestTelemetry.Duration = $Duration
        $requestTelemetry.Name = $Name
        $requestTelemetry.ResponseCode = $responseCode
        $requestTelemetry.Success = $success
        $requestTelemetry.Timestamp = $StartTime

        if ($properties.Count -ge 1) {
            Write-Verbose ("Received '$($properties.Count)' properties to add to the request.")
            foreach ($key in $properties.Keys) {
                Write-Verbose ("Received '$($key)' property to add to the request.")
                $requestTelemetry.Properties[$key] = $properties[$key]
            }
        }

        if (-not [string]::IsNullOrWhiteSpace($url)) {
            Write-Verbose ("Received '$($url)' url for the request")
            $requestTelemetry.Url = $url
        }

        $client.TrackRequest($requestTelemetry)
    }
    END {
        $Client.Flush()
    }
}

Export-ModuleMember -Function Write-ApplicationInsightsRequest
Function Invoke-ApplicationInsightsMeasuredCommand {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [scriptblock]
        $scriptblock,

        [Parameter(Mandatory = $true)]
        [ValidateNotNullOrEmpty()]
        [string]
        $name
    )
    BEGIN {
        Write-Verbose ("Received '$($name)' name for the command")
    }
    PROCESS {

        $success = $true
        $statusCode = "200"

        $startDate = [System.DateTime]::UtcNow

        try {
            $retVal = $scriptblock.Invoke()
        }
        catch [System.Exception] {
            Write-Verbose ("Caught exception in the scriptblock")
            $statusCode = $_
            $success = $false
            throw
        }
        finally {
            $endDate = [System.DateTime]::UtcNow
        }

        $duration = New-TimeSpan -Start $startDate -End $endDate

        Write-Verbose ("Received '$($duration)' duration for the command")
        Write-ApplicationInsightsRequest -Name $name -StartTime $startDate -Duration $duration -responseCode $statusCode -success $success
    }
    END {
        return $retVal
    }
}

Export-ModuleMember -Function Invoke-ApplicationInsightsMeasuredCommand