Skip to content

Commit e414357

Browse files
App insights test
1 parent 6a7a62d commit e414357

File tree

5 files changed

+228
-3
lines changed

5 files changed

+228
-3
lines changed

Modules/CIPPCore/Public/Entrypoints/HTTP Functions/New-CippCoreRequest.ps1

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,31 @@ function New-CippCoreRequest {
6969
Write-Information "Access: $Access"
7070
Write-LogMessage -headers $Headers -API $Request.Params.CIPPEndpoint -message 'Accessed this API' -Sev 'Debug'
7171
if ($Access) {
72-
$Response = & $FunctionName @HttpTrigger
72+
# Prepare telemetry metadata for HTTP API call
73+
$metadata = @{
74+
Endpoint = $Request.Params.CIPPEndpoint
75+
FunctionName = $FunctionName
76+
Method = $Request.Method
77+
TriggerType = 'HTTP'
78+
}
79+
80+
# Add tenant filter if present
81+
if ($Request.Query.TenantFilter) {
82+
$metadata['Tenant'] = $Request.Query.TenantFilter
83+
} elseif ($Request.Body.TenantFilter) {
84+
$metadata['Tenant'] = $Request.Body.TenantFilter
85+
}
86+
87+
# Add user info if available
88+
if ($Request.Headers.'x-ms-client-principal-name') {
89+
$metadata['User'] = $Request.Headers.'x-ms-client-principal-name'
90+
}
91+
92+
# Wrap the API call execution with telemetry
93+
$Response = Measure-CippTask -TaskName $Request.Params.CIPPEndpoint -Metadata $metadata -Script {
94+
& $FunctionName @HttpTrigger
95+
}
96+
7397
# Filter to only return HttpResponseContext objects
7498
$HttpResponse = $Response | Where-Object { $_.PSObject.TypeNames -eq 'Microsoft.Azure.Functions.PowerShellWorker.HttpResponseContext' }
7599
if ($HttpResponse) {
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
function Measure-CippTask {
2+
<#
3+
.SYNOPSIS
4+
Measure and track CIPP task execution with Application Insights telemetry
5+
.DESCRIPTION
6+
Wraps task execution in a timer, sends custom event to Application Insights with duration and metadata
7+
.PARAMETER TaskName
8+
The name of the task being executed (e.g., "New-CIPPTemplateRun")
9+
.PARAMETER Script
10+
The scriptblock to execute and measure
11+
.PARAMETER Metadata
12+
Optional hashtable of metadata to include in telemetry (e.g., Command, Tenant, TaskInfo)
13+
.FUNCTIONALITY
14+
Internal
15+
.EXAMPLE
16+
Measure-CippTask -TaskName "ApplyTemplate" -Script {
17+
# Task logic here
18+
} -Metadata @{
19+
Command = "New-CIPPTemplateRun"
20+
Tenant = "contoso.onmicrosoft.com"
21+
}
22+
#>
23+
[CmdletBinding()]
24+
param(
25+
[Parameter(Mandatory = $true)]
26+
[string]$TaskName,
27+
28+
[Parameter(Mandatory = $true)]
29+
[scriptblock]$Script,
30+
31+
[Parameter(Mandatory = $false)]
32+
[hashtable]$Metadata
33+
)
34+
35+
# Initialize tracking variables
36+
$sw = [System.Diagnostics.Stopwatch]::StartNew()
37+
$result = $null
38+
$errorOccurred = $false
39+
$errorMessage = $null
40+
41+
try {
42+
# Execute the actual task
43+
$result = & $Script
44+
} catch {
45+
$errorOccurred = $true
46+
$errorMessage = $_.Exception.Message
47+
# Re-throw to preserve original error behavior
48+
throw
49+
} finally {
50+
# Stop the timer
51+
$sw.Stop()
52+
$durationMs = [int]$sw.Elapsed.TotalMilliseconds
53+
54+
# Send telemetry if TelemetryClient is available
55+
if ($global:TelemetryClient) {
56+
try {
57+
# Build properties dictionary for customDimensions
58+
$props = New-Object 'System.Collections.Generic.Dictionary[string,string]'
59+
$props['TaskName'] = $TaskName
60+
$props['Success'] = (-not $errorOccurred).ToString()
61+
62+
if ($errorOccurred) {
63+
$props['ErrorMessage'] = $errorMessage
64+
}
65+
66+
# Add all metadata to properties
67+
if ($Metadata) {
68+
foreach ($key in $Metadata.Keys) {
69+
$value = $Metadata[$key]
70+
# Convert value to string, handling nulls
71+
if ($null -ne $value) {
72+
$props[$key] = [string]$value
73+
} else {
74+
$props[$key] = ''
75+
}
76+
}
77+
}
78+
79+
# Metrics dictionary for customMeasurements
80+
$metrics = New-Object 'System.Collections.Generic.Dictionary[string,double]'
81+
$metrics['DurationMs'] = [double]$durationMs
82+
83+
# Send custom event to Application Insights
84+
$global:TelemetryClient.TrackEvent('CIPP.TaskCompleted', $props, $metrics)
85+
$global:TelemetryClient.Flush()
86+
87+
Write-Verbose "Telemetry sent for task '$TaskName' (${durationMs}ms)"
88+
} catch {
89+
Write-Warning "Failed to send telemetry for task '${TaskName}': $($_.Exception.Message)"
90+
}
91+
} else {
92+
Write-Verbose "TelemetryClient not initialized, skipping telemetry for task '$TaskName'"
93+
}
94+
}
95+
96+
return $result
97+
}

Modules/CippEntrypoints/CippEntrypoints.psm1

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,9 +294,52 @@ function Receive-CippActivityTrigger {
294294

295295
if ($Item.FunctionName) {
296296
$FunctionName = 'Push-{0}' -f $Item.FunctionName
297+
298+
# Prepare telemetry metadata
299+
$taskName = if ($Item.Command) { $Item.Command } else { $FunctionName }
300+
$metadata = @{
301+
Command = if ($Item.Command) { $Item.Command } else { $FunctionName }
302+
FunctionName = $FunctionName
303+
}
304+
305+
# Add tenant information if available
306+
if ($Item.TaskInfo) {
307+
if ($Item.TaskInfo.Tenant) {
308+
$metadata['Tenant'] = $Item.TaskInfo.Tenant
309+
}
310+
if ($Item.TaskInfo.Name) {
311+
$metadata['JobName'] = $Item.TaskInfo.Name
312+
}
313+
if ($Item.TaskInfo.Recurrence) {
314+
$metadata['Recurrence'] = $Item.TaskInfo.Recurrence
315+
}
316+
}
317+
318+
# Add tenant from other common fields
319+
if (-not $metadata['Tenant']) {
320+
if ($Item.TenantFilter) {
321+
$metadata['Tenant'] = $Item.TenantFilter
322+
} elseif ($Item.Tenant) {
323+
$metadata['Tenant'] = $Item.Tenant
324+
}
325+
}
326+
327+
# Add queue information
328+
if ($Item.QueueId) {
329+
$metadata['QueueId'] = $Item.QueueId
330+
}
331+
if ($Item.QueueName) {
332+
$metadata['QueueName'] = $Item.QueueName
333+
}
334+
297335
try {
298336
Write-Warning "Activity starting Function: $FunctionName."
299-
$Output = Invoke-Command -ScriptBlock { & $FunctionName -Item $Item }
337+
338+
# Wrap the function execution with telemetry
339+
$Output = Measure-CippTask -TaskName $taskName -Metadata $metadata -Script {
340+
Invoke-Command -ScriptBlock { & $FunctionName -Item $Item }
341+
}
342+
300343
Write-Warning "Activity completed Function: $FunctionName."
301344
if ($TaskStatus) {
302345
$QueueTask.Status = 'Completed'
@@ -389,7 +432,31 @@ function Receive-CIPPTimerTrigger {
389432
$Parameters = $Function.Parameters | ConvertTo-Json | ConvertFrom-Json -AsHashtable
390433
}
391434

392-
$Results = Invoke-Command -ScriptBlock { & $Function.Command @Parameters }
435+
# Prepare telemetry metadata
436+
$metadata = @{
437+
Command = $Function.Command
438+
Cron = $Function.Cron
439+
FunctionId = $Function.Id
440+
TriggerType = 'Timer'
441+
}
442+
443+
# Add parameters if available
444+
if ($Parameters.Count -gt 0) {
445+
$metadata['ParameterCount'] = $Parameters.Count
446+
# Add specific known parameters
447+
if ($Parameters.Tenant) {
448+
$metadata['Tenant'] = $Parameters.Tenant
449+
}
450+
if ($Parameters.TenantFilter) {
451+
$metadata['Tenant'] = $Parameters.TenantFilter
452+
}
453+
}
454+
455+
# Wrap the timer function execution with telemetry
456+
$Results = Measure-CippTask -TaskName $Function.Command -Metadata $metadata -Script {
457+
Invoke-Command -ScriptBlock { & $Function.Command @Parameters }
458+
}
459+
393460
if ($Results -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$') {
394461
$FunctionStatus.OrchestratorId = $Results -join ','
395462
$Status = 'Started'
377 KB
Binary file not shown.

profile.ps1

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,42 @@
11
Write-Information '#### CIPP-API Start ####'
22

3+
# Load Application Insights SDK for telemetry
4+
Set-Location -Path $PSScriptRoot
5+
try {
6+
$AppInsightsDllPath = Join-Path $PSScriptRoot 'Shared\AppInsights\Microsoft.ApplicationInsights.dll'
7+
if (Test-Path $AppInsightsDllPath) {
8+
[Reflection.Assembly]::LoadFile($AppInsightsDllPath) | Out-Null
9+
Write-Information 'Application Insights SDK loaded successfully'
10+
} else {
11+
Write-Warning "Application Insights DLL not found at: $AppInsightsDllPath"
12+
}
13+
} catch {
14+
Write-Warning "Failed to load Application Insights SDK: $($_.Exception.Message)"
15+
}
16+
17+
# Initialize global TelemetryClient
18+
if (-not $global:TelemetryClient) {
19+
try {
20+
$connectionString = $env:APPLICATIONINSIGHTS_CONNECTION_STRING
21+
if ($connectionString) {
22+
# Use connection string (preferred method)
23+
$config = [Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration]::CreateDefault()
24+
$config.ConnectionString = $connectionString
25+
$global:TelemetryClient = [Microsoft.ApplicationInsights.TelemetryClient]::new($config)
26+
Write-Information 'TelemetryClient initialized with connection string'
27+
} elseif ($env:APPINSIGHTS_INSTRUMENTATIONKEY) {
28+
# Fall back to instrumentation key
29+
$global:TelemetryClient = [Microsoft.ApplicationInsights.TelemetryClient]::new()
30+
$global:TelemetryClient.InstrumentationKey = $env:APPINSIGHTS_INSTRUMENTATIONKEY
31+
Write-Information 'TelemetryClient initialized with instrumentation key'
32+
} else {
33+
Write-Warning 'No Application Insights connection string or instrumentation key found'
34+
}
35+
} catch {
36+
Write-Warning "Failed to initialize TelemetryClient: $($_.Exception.Message)"
37+
}
38+
}
39+
340
# Import modules
441
@('CIPPCore', 'CippExtensions', 'Az.KeyVault', 'Az.Accounts', 'AzBobbyTables') | ForEach-Object {
542
try {

0 commit comments

Comments
 (0)