This guide explains how to configure Application Insights for SecureBootDashboard to monitor performance, track usage, and diagnose issues - even when deployed outside Azure (on-premises or other cloud providers).
Application Insights provides:
- Performance Monitoring: Track response times, throughput, and dependencies
- Usage Analytics: Monitor user behavior, page views, and custom events
- Error Tracking: Capture exceptions and failed requests
- Live Metrics: Real-time monitoring dashboard
- Custom Telemetry: Track business-specific metrics
???????????????????????????????????????????????????????????????
? SecureBootDashboard Components ?
???????????????????????????????????????????????????????????????
? ??????????????? ??????????????? ???????????????????? ?
? ? Web (UI) ? ? Api (REST) ? ? Client (Agent) ? ?
? ? Razor ? ? ASP.NET ? ? .NET Fx 4.8 ? ?
? ? Pages ? ? Core 10 ? ? ? ?
? ??????????????? ??????????????? ???????????????????? ?
? ? ? ? ?
? ??????????????????????????????????????? ?
? ? ?
? ? ?
? ????????????????????????? ?
? ? Application Insights ? ?
? ? Telemetry SDK ? ?
? ????????????????????????? ?
? ? ?
????????????????????????????????????????????????????????????????
?
?
????????????????????????????????????????
? Application Insights Service ?
? (Azure or On-Premises Collector) ?
????????????????????????????????????????
?
?
????????????????????????????????????????
? Azure Portal / Custom Dashboard ?
? - Performance metrics ?
? - Usage analytics ?
? - Error tracking ?
? - Custom queries (KQL) ?
????????????????????????????????????????
Best for:
- Azure-hosted deployments
- Cloud-first scenarios
- No infrastructure management
# Using Azure CLI
az monitor app-insights component create \
--app secureboot-dashboard-insights \
--location eastus \
--resource-group rg-secureboot-prod \
--application-type web
# Get connection string
az monitor app-insights component show \
--app secureboot-dashboard-insights \
--resource-group rg-secureboot-prod \
--query connectionString --output tsvOutput:
InstrumentationKey=12345678-1234-1234-1234-123456789012;IngestionEndpoint=https://eastus-1.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/
Environment Variable (Recommended):
# Linux/macOS
export APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=...;IngestionEndpoint=..."
# Windows PowerShell
$env:APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=...;IngestionEndpoint=..."
# Windows CMD
set APPLICATIONINSIGHTS_CONNECTION_STRING=InstrumentationKey=...;IngestionEndpoint=...Or in appsettings.Production.json:
{
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=12345678-1234-1234-1234-123456789012;IngestionEndpoint=https://eastus-1.in.applicationinsights.azure.com/;LiveEndpoint=https://eastus.livediagnostics.monitor.azure.com/"
}
}Best for:
- Air-gapped environments
- Data sovereignty requirements
- Self-hosted infrastructure
Use OpenTelemetry Collector or Application Insights Local Forwarder:
# Docker deployment example
docker run -d \
--name appinsights-collector \
-p 55678:55678 \
-p 4318:4318 \
-e APPLICATIONINSIGHTS_CONNECTION_STRING="InstrumentationKey=local;IngestionEndpoint=http://localhost:4318/" \
otel/opentelemetry-collector-contrib:latest{
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=local;IngestionEndpoint=http://your-collector:4318/",
"EndpointAddress": "http://your-collector:4318/v2/track"
}
}Development (appsettings.Development.json):
{
"ApplicationInsights": {
"ConnectionString": ""
}
}Production (appsettings.Production.json):
{
"ApplicationInsights": {
"ConnectionString": "InstrumentationKey=...;IngestionEndpoint=..."
}
}appsettings.json:
{
"ApplicationInsights": {
"ConnectionString": "",
"EnableAdaptiveSampling": true,
"EnablePerformanceCounterCollectionModule": true,
"EnableQuickPulseMetricStream": true,
"CloudRoleName": "SecureBootDashboard.Web"
}
}What gets tracked:
- Page view duration
- Server response times
- Failed requests (4xx, 5xx)
- Dependency calls to API
- Browser timing (if JavaScript SDK added)
appsettings.json:
{
"ApplicationInsights": {
"ConnectionString": "",
"EnableAdaptiveSampling": true,
"EnablePerformanceCounterCollectionModule": true,
"EnableQuickPulseMetricStream": true,
"CloudRoleName": "SecureBootDashboard.Api"
}
}What gets tracked:
- HTTP request duration
- SQL query performance (via EF Core)
- Azure Queue operations
- SignalR connection events
- Background service performance
- Custom events (e.g., "Device Cleanup Started")
Not yet implemented - Consider adding for:
- Report submission success/failure
- Certificate enumeration performance
- Client errors and exceptions
Future implementation:
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Extensibility;
var config = TelemetryConfiguration.CreateDefault();
config.ConnectionString = "InstrumentationKey=...;IngestionEndpoint=...";
var telemetryClient = new TelemetryClient(config);
telemetryClient.TrackEvent("ReportSubmitted",
properties: new Dictionary<string, string>
{
{ "DeviceName", Environment.MachineName },
{ "Version", clientVersion }
});| Metric | Query (Kusto/KQL) | Alert Threshold |
|---|---|---|
| API Response Time (P95) | requests | where cloud_RoleName == "SecureBootDashboard.Api" | summarize percentile(duration, 95) by bin(timestamp, 5m) |
> 500ms |
| Web Page Load Time | pageViews | summarize avg(duration) by name |
> 2s |
| Database Query Duration | dependencies | where type == "SQL" | summarize avg(duration) by name |
> 100ms |
| Failed Requests | requests | where success == false | summarize count() by resultCode |
> 10/min |
| Metric | Query | Business Value |
|---|---|---|
| Active Devices | customEvents | where name == "ReportSubmitted" | summarize dcount(tostring(customDimensions.DeviceName)) by bin(timestamp, 1d) |
Fleet coverage |
| Dashboard Users | pageViews | summarize dcount(user_Id) by bin(timestamp, 1d) |
Adoption rate |
| API Calls/Hour | requests | where cloud_RoleName == "SecureBootDashboard.Api" | summarize count() by bin(timestamp, 1h) |
System load |
| Metric | Query | Action |
|---|---|---|
| Exception Rate | exceptions | summarize count() by type |
Investigate top 3 |
| Queue Failures | customEvents | where name == "QueueProcessingFailed" | summarize count() |
Check queue health |
| Certificate Validation Errors | traces | where message contains "Certificate validation failed" | summarize count() |
Review mTLS config |
In DeviceCleanupService.cs:
using Microsoft.ApplicationInsights;
public class DeviceCleanupService
{
private readonly TelemetryClient _telemetryClient;
public DeviceCleanupService(TelemetryClient telemetryClient)
{
_telemetryClient = telemetryClient;
}
public async Task<int> CleanupInactiveDevicesAsync(int daysInactive)
{
var startTime = DateTime.UtcNow;
try
{
var deletedCount = await PerformCleanup(daysInactive);
// Track success
_telemetryClient.TrackEvent("DeviceCleanupCompleted",
properties: new Dictionary<string, string>
{
{ "DaysInactive", daysInactive.ToString() },
{ "DevicesDeleted", deletedCount.ToString() }
},
metrics: new Dictionary<string, double>
{
{ "Duration", (DateTime.UtcNow - startTime).TotalMilliseconds }
});
return deletedCount;
}
catch (Exception ex)
{
_telemetryClient.TrackException(ex,
properties: new Dictionary<string, string>
{
{ "Operation", "DeviceCleanup" },
{ "DaysInactive", daysInactive.ToString() }
});
throw;
}
}
}In SecureBootReportsController.cs:
[HttpPost]
public async Task<IActionResult> SubmitReport([FromBody] SecureBootStatusReport report)
{
// ...existing validation...
// Track deployment state distribution
_telemetryClient.TrackMetric("SecureBootDeploymentState",
value: (int)report.DeploymentState,
properties: new Dictionary<string, string>
{
{ "State", report.DeploymentState.ToString() },
{ "FleetId", report.Device.FleetId ?? "Unknown" }
});
// Track certificate issues
if (report.DeploymentState != DeploymentState.Compliant)
{
_telemetryClient.TrackEvent("NonCompliantDeviceReported",
properties: new Dictionary<string, string>
{
{ "DeviceName", report.Device.MachineName },
{ "State", report.DeploymentState.ToString() },
{ "MissingCerts", string.Join(",", GetMissingCertificates(report)) }
});
}
return Ok();
}// Live metrics for last 5 minutes
requests
| where timestamp > ago(5m)
| where cloud_RoleName in ("SecureBootDashboard.Web", "SecureBootDashboard.Api")
| summarize
TotalRequests = count(),
AvgDuration = avg(duration),
P95Duration = percentile(duration, 95),
FailedRequests = countif(success == false)
by cloud_RoleName, bin(timestamp, 1m)
| render timechartrequests
| where cloud_RoleName == "SecureBootDashboard.Api"
| where timestamp > ago(1d)
| summarize
Count = count(),
AvgDuration = avg(duration),
P95Duration = percentile(duration, 95),
MaxDuration = max(duration)
by name
| where P95Duration > 500 // Endpoints slower than 500ms
| order by P95Duration desc
| take 10customEvents
| where name == "ReportSubmitted"
| where timestamp > ago(7d)
| extend DeviceName = tostring(customDimensions.DeviceName)
| summarize ReportCount = count() by bin(timestamp, 1h)
| render timechartexceptions
| where timestamp > ago(24h)
| summarize Count = count() by type, bin(timestamp, 1h)
| render columnchart1. High Error Rate
requests
| where timestamp > ago(5m)
| where success == false
| summarize FailureRate = (count() * 100.0) / toscalar(requests | where timestamp > ago(5m) | count())
| where FailureRate > 5 // Alert if >5% failuresNotification: Email + SMS to on-call engineer
2. API Latency Spike
requests
| where cloud_RoleName == "SecureBootDashboard.Api"
| where timestamp > ago(10m)
| summarize P95Duration = percentile(duration, 95)
| where P95Duration > 1000 // Alert if P95 > 1sNotification: Slack channel #alerts
3. Database Connection Failures
dependencies
| where type == "SQL"
| where success == false
| where timestamp > ago(5m)
| summarize FailureCount = count()
| where FailureCount > 10Notification: PagerDuty incident
1. Queue Processing Delay
customEvents
| where name == "QueueMessageProcessed"
| extend QueueDelay = todouble(customDimensions.DelayMs)
| where timestamp > ago(15m)
| summarize AvgDelay = avg(QueueDelay)
| where AvgDelay > 30000 // Warn if avg delay > 30s2. Low Device Report Volume
customEvents
| where name == "ReportSubmitted"
| where timestamp > ago(1h)
| summarize ReportCount = count()
| where ReportCount < 10 // Warn if <10 reports/hourAdaptive Sampling (Default):
- Automatically adjusts sampling rate based on volume
- Keeps all errors and exceptions
- Reduces telemetry cost by 60-80%
Fixed Sampling:
{
"ApplicationInsights": {
"EnableAdaptiveSampling": false,
"SamplingSettings": {
"MaxTelemetryItemsPerSecond": 5
}
}
}| Data Type | Default Retention | Extended Retention |
|---|---|---|
| Raw Telemetry | 90 days | Up to 730 days (paid) |
| Aggregated Metrics | 90 days | 90 days |
| Log Analytics | Configurable | Up to 2 years |
Cost Estimate:
- 1 GB ingested: ~$2.30/month
- Typical deployment: 5-10 GB/month = ~$12-25/month
appsettings.json:
{
"ApplicationInsights": {
"EnableAuthenticationTrackingJavaScript": false,
"DisableTelemetry": false,
"RequestCollectionOptions": {
"InjectResponseHeaders": false
}
}
}Program.cs:
builder.Services.AddApplicationInsightsTelemetry(options =>
{
// Exclude IP addresses
options.EnableAdaptiveSampling = true;
});
builder.Services.AddApplicationInsightsTelemetryProcessor<SensitiveDataFilterTelemetryProcessor>();
// Custom processor
public class SensitiveDataFilterTelemetryProcessor : ITelemetryProcessor
{
public void Process(ITelemetry item)
{
if (item is RequestTelemetry request)
{
// Remove IP address
request.Context.Location.Ip = "0.0.0.0";
// Redact query strings with passwords
if (request.Url?.Query?.Contains("password") == true)
{
request.Url = new Uri(request.Url.GetLeftPart(UriPartial.Path));
}
}
}
}Symptoms:
- No telemetry visible in Azure Portal
- Logs show "Application Insights: Disabled"
Solutions:
- Verify connection string is set:
echo $APPLICATIONINSIGHTS_CONNECTION_STRING
- Check firewall rules allow outbound HTTPS to:
*.in.applicationinsights.azure.com*.livediagnostics.monitor.azure.com
- Enable verbose logging:
{ "Logging": { "ApplicationInsights": { "LogLevel": { "Default": "Debug" } } } }
Symptoms:
- Unexpected costs
- Sampling rate very low (<10%)
Solutions:
- Review top telemetry contributors:
union * | where timestamp > ago(1d) | summarize Count = count() by itemType, cloud_RoleName | order by Count desc
- Exclude noisy endpoints:
builder.Services.Configure<TelemetryConfiguration>(config => { config.TelemetryProcessorChainBuilder .UseAdaptiveSampling(excludedTypes: "Event;Exception") .Build(); });
PowerShell Script:
# Test-ApplicationInsights.ps1
$connectionString = $env:APPLICATIONINSIGHTS_CONNECTION_STRING
if ([string]::IsNullOrEmpty($connectionString)) {
Write-Error "APPLICATIONINSIGHTS_CONNECTION_STRING not set"
exit 1
}
Write-Host "Testing Application Insights connectivity..." -ForegroundColor Cyan
# Start application
Start-Process "dotnet" -ArgumentList "run --project SecureBootDashboard.Api" -NoNewWindow
Start-Sleep 10
# Generate test traffic
for ($i = 1; $i -le 20; $i++) {
Invoke-RestMethod -Uri "https://localhost:5001/api/Devices" -SkipCertificateCheck
Start-Sleep 1
}
Write-Host "? Test traffic generated" -ForegroundColor Green
Write-Host " Check Azure Portal in 2-3 minutes for telemetry" -ForegroundColor Yellow- ? NuGet Packages Installed - Application Insights SDK added
- ? Configuration Added - appsettings.json updated
- ? Serilog Integration - Logs forwarded to App Insights
- ? Set Connection String - Configure for your environment
- ? Create Alerts - Set up critical monitoring
- ? Build Dashboard - Custom KQL queries in Azure Portal
Status: ? Configuration Complete
Version: 1.12.0
Last Updated: 2025-01-23