PSCoreApplicationInsights.psm1
function New-ApplicationInsightsClient { <# .SYNOPSIS Create a new Application insights Client .DESCRIPTION Create a new Application insights Client by supplying an Instrumentation Key of your Application Insights instance. .PARAMETER InstrumentationKey The Instrumentation Key of your Application Insights instance. .EXAMPLE New-ApplicationInsightsClient -InstrumentationKey c323cf10-da34-4a73-9eac-000000000000 Create a new Application Insights Telemetry Client and store it in $global:AIClient .EXAMPLE $client = New-ApplicationInsightsClient -InstrumentationKey c323cf10-da34-4a73-9eac-000000000000 Create a new Application Insights Telemetry Client and store it in a variable. .NOTES General notes #> [CmdletBinding(SupportsShouldProcess)] 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 ) if ($PSCmdlet.ShouldProcess([Microsoft.ApplicationInsights.TelemetryClient], "New")) { $global:AIClient = [Microsoft.ApplicationInsights.TelemetryClient]::new() } if ($PSCmdlet.ShouldProcess('$global:AIClient', "Set InstrumentationKey")) { $global:AIClient.InstrumentationKey = $InstrumentationKey } $defaultUserInformation = @{ AuthenticatedUserId = whoami UserAgent = ("PS $($psversiontable.PSEdition) $($psversiontable.PSVersion)") } $defaultDeviceInformation = @{ OperatingSystem = $psversiontable.OS } if ($PSCmdlet.ShouldProcess('$global:AIClient', 'Set-ApplicationInsightsClientInformation')) { $global:AIClient = Set-ApplicationInsightsClientInformation -UserInformation $defaultUserInformation -DeviceInformation $defaultDeviceInformation -Client $global:AIClient -WhatIf:$WhatIfPreference } return $global:AIClient } Export-ModuleMember -Function New-ApplicationInsightsClient function Confirm-ApplicationInsightsClient { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [Microsoft.ApplicationInsights.TelemetryClient] $Client ) if ($null -eq $client) { if ($null -eq $global:AIClient) { write-error ("No Application insight client defined. Please use 'New-ApplicationInsightsClient' to create one.") return; } else { $client = $global:AIClient } } [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 { <# .SYNOPSIS Changes the Telemetry Client information .DESCRIPTION Changes the Telemetry Client information. This currently supports changing the user and Device information. This information will be displayed in the Application Insights logging. .PARAMETER Client The Application Insights Telemetry Client. Defaults to $global:AIClient .PARAMETER UserInformation A hashtable with User Information To find valid properties, Create a client and look at the current properties. $global:AIClient.context.User .PARAMETER DeviceInformation A hashtable with device information. To find valid properties, Create a client and look at the current properties. $global:AIClient.context.Device .EXAMPLE $userInformation = @{AuthenticatedUserId = "John Doe"; UserAgent = "PS Core 7.2.5"} ; Set-ApplicationInsightsClientInformation -UserInformation $userInformation .NOTES Default settings are already applied when creating a new Application Insight client. #> [CmdletBinding(SupportsShouldProcess)] param ( [Parameter(Mandatory = $false)] [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") } if ($null -eq $client) { if ($null -eq $global:AIClient) { write-error ("No Application insight client defined. Please use 'New-ApplicationInsightsClient' to create one.") return; } else { $client = $global:AIClient } } } 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])'") if ($PSCmdlet.ShouldProcess("$Client.Context.User.$property", "$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])'") if ($PSCmdlet.ShouldProcess("$Client.Context.User.$property", "$UserInformation[$property]")) { $Client.Context.Device.$property = $DeviceInformation[$property] } } } } } end { return $Client } } Export-ModuleMember -Function Set-ApplicationInsightsClientInformation function Write-ApplicationInsightsTrace { <# .SYNOPSIS Write a simple Trace message to the Application Insights service. .DESCRIPTION Write a simple Trace message to the Application Insights service. Supports several Severity levels .PARAMETER Client This is the Telemetry Client used to send the message. If not specified, Defaults to "$global:AICient" .PARAMETER Message The message you want to send to Application Insights. .PARAMETER SeverityLevel The severity level of the message. Default is 'Information'. Allowed values: 'Verbose', 'Information', 'Warning', 'Error', 'Critical' .PARAMETER properties A Dictionary of properties you want to send with the message. .EXAMPLE Write-ApplicationInsightsTrace -Client $client -Message "This is a test message as Critical" -SeverityLevel "Critical" .EXAMPLE $properties = [System.Collections.Generic.Dictionary[string, string]]::new() $properties.Add("target", "azkv-powershell-001") $properties.Add("type", "Keyvault") Write-ApplicationInsightsTrace -Client $client -Message "Created new keyvault" -SeverityLevel "Information" -properties $properties #> [CmdletBinding()] param ( [Parameter(Mandatory = $false, HelpMessage = 'This is the Telemetry Client used to send the message. If not specified, Defaults to "$global:AICient"')] [Microsoft.ApplicationInsights.TelemetryClient] $Client, [Parameter(Mandatory = $true, HelpMessage = "This is the message being send to Application Insights.")] [string] $Message, [Parameter(Mandatory = $false, HelpMessage = "This is the Severity Level of the message and will show as 0..5 in Application Insights")] [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 ($null -eq $client) { if ($null -eq $global:AIClient) { write-error ("No Application insight client defined. Please use 'New-ApplicationInsightsClient' to create one.") return; } else { $client = $global:AIClient } } 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) Write-Verbose ("Sent message '$($Message)' with '$($properties.Count)' properties to Application Insights.") } else { $Client.TrackTrace($Message, [Microsoft.ApplicationInsights.DataContracts.SeverityLevel]::$($SeverityLevel)) Write-Verbose ("Sent message '$($Message)' to Application Insights.") } } END { $Client.Flush() Write-Verbose ("Client Flushed") } } Export-ModuleMember -Function Write-ApplicationInsightsTrace function Write-ApplicationInsightsMetric { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [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 ($null -eq $client) { if ($null -eq $global:AIClient) { write-error ("No Application insight client defined. Please use 'New-ApplicationInsightsClient' to create one.") return; } else { $client = $global:AIClient } } 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) Write-Verbose ("Sent metric '$($Name)' with '$($Metric)' value and '$($properties.Count)' properties to Application Insights.") } else { $client.TrackMetric($name, $Metric) Write-Verbose ("Sent metric '$($Name)' with '$($Metric)' value to Application Insights.") } } END { $Client.Flush() Write-Verbose ("Client Flushed") } } Export-ModuleMember -Function Write-ApplicationInsightsMetric function Write-ApplicationInsightsException { [CmdletBinding(DefaultParameterSetName = "Exception")] param ( [Parameter(Mandatory = $false)] [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 ($null -eq $client) { if ($null -eq $global:AIClient) { write-error ("No Application insight client defined. Please use 'New-ApplicationInsightsClient' to create one.") return; } else { $client = $global:AIClient } } if ($PSCmdlet.ParameterSetName -eq "StringException") { $Exception = [System.Exception]::new($ExceptionString) } } PROCESS { $client.TrackException($Exception, $properties, $Metrics) Write-Verbose ("Sent exception '$($Exception)' with '$($Metrics.Count)' metrics and '$($properties.Count)' properties to Application Insights.") } END { $Client.Flush() Write-Verbose ("Client Flushed") } } Export-ModuleMember -Function Write-ApplicationInsightsException function Write-ApplicationInsightsRequest { [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [Microsoft.ApplicationInsights.TelemetryClient] $Client, [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' property 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") if ($null -eq $client) { if ($null -eq $global:AIClient) { write-error ("No Application insight client defined. Please use 'New-ApplicationInsightsClient' to create one.") return; } else { Write-Verbose ("Using global client") $client = $global:AIClient } } else { Write-Verbose ("Using supplied client") } } PROCESS { $requestTelemetry = [Microsoft.ApplicationInsights.DataContracts.RequestTelemetry]::new() $requestTelemetry.Duration = $Duration $requestTelemetry.Name = $Name $requestTelemetry.ResponseCode = $responseCode $requestTelemetry.Success = $success $requestTelemetry.Timestamp = $StartTime $client.Context.Operation.Name = $Name $client.Context.Operation.Id = [guid]::NewGuid().Guid 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 } Write-Verbose ("Sending request telemetry") $client.TrackRequest($requestTelemetry) } END { $Client.Flush() Write-Verbose ("Client Flushed") } } Export-ModuleMember -Function Write-ApplicationInsightsRequest Function Invoke-ApplicationInsightsMeasuredCommand { <# .SYNOPSIS Invoke a scriptblock that is measured by Application Insights. .DESCRIPTION Invoke a scriptblock that is measured by Application Insights. This created a timespan and writes the timing to Application Insights. The output of the scriptblock is returned. .PARAMETER Client The Application Insights Telemetry Client. Defaults to $global:AIClient .PARAMETER scriptblock The scriptblock you wish to execute and measure. .PARAMETER name This is a name you wish to give to the scriptblock. This is used to identify the scriptblock in Application Insights. .EXAMPLE Invoke-ApplicationInsightsMeasuredCommand -ScriptBlock { start-sleep -Milliseconds 150 } -Name "Performing task X" #> [CmdletBinding()] param ( [Parameter(Mandatory = $false)] [Microsoft.ApplicationInsights.TelemetryClient] $Client, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [scriptblock] $scriptblock, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string] $name ) BEGIN { if ($null -eq $client) { if ($null -eq $global:AIClient) { write-error ("No Application insight client defined. Please use 'New-ApplicationInsightsClient' to create one.") return; } else { $client = $global:AIClient } } 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)' as duration for the command") Write-ApplicationInsightsRequest -Name $name -StartTime $startDate -Duration $duration -responseCode $statusCode -success $success } END { $Client.Flush() Write-Verbose ("Client Flushed") return $retVal } } Export-ModuleMember -Function Invoke-ApplicationInsightsMeasuredCommand |