Attach version and build information to Application Insights telemetry
Every Application Insights item has an application_Version column (AppVersion in the workspace), and filling it from your build is what lets you compare failure rates and latency before and after a deployment. In the classic 2.x SDK it's Context.Component.Version, set from BuildInfo.config, the ApplicationVersion option or an initializer. With SDK 3.x and the OpenTelemetry exporters it comes from the service.version resource attribute, which Microsoft's .NET exporter maps to Application Version, and SDK 3.x defaults it to the entry assembly's version. Pair it with release annotations so deployments show on the charts.
“Did the last deployment make it worse?” is the most common question asked of an APM tool, and it’s only answerable if every item knows which build produced it. Application Insights has a column for exactly that. It’s empty more often than not.
The column
Microsoft’s data model keeps application_Version (AppVersion in workspace tables) in the application context, which is always about the app sending the telemetry, and describes its purpose as analysing trend changes in the app’s behaviour and their correlation with deployments. On the wire it’s the ai.application.ver tag. It’s a free-form string: whatever you put there is what you can filter and group by.
Choose a version that works in queries
The argument between semantic versions and automatic build numbers matters less than three properties of whatever you choose:
- Unique per build. If a hotfix ships under the same version as the build it fixes, no query can separate them. A semantic version alone breaks this the first time someone redeploys without bumping it; a build number or commit hash never does.
- Identical on every instance. The value must come from the build, not from the moment the process started. A timestamp taken at startup gives each instance its own “version” and turns one deployment into dozens.
- Sortable or at least recognisable. People read this column in charts and incident channels.
2026.10.21.3or4.2.0+a01dbeftells them something; a bare hash makes them look it up.
A common compromise is a semantic version for people plus build metadata for machines, in one string. Keep the environment (development, staging, production) out of it: the same build moves between environments, and the version should prove it’s the same build.
For environments, the simplest separation is a resource per environment, which also keeps test traffic out of production alerts and costs. When several environments must share a resource, record the environment as its own property on every item, as in the code below, so you can filter on it without parsing versions.
Classic SDK 2.x
The classic SDK fills Component.Version in one of three ways:
BuildInfoConfigComponentVersionTelemetryInitializer(from theWindowsServerpackage that classic ASP.NET apps use) reads the version from theBuildInfo.configfile that MSBuild can produce, and stamps it on every item.- The
ApplicationVersionoption onApplicationInsightsServiceOptionsin the ASP.NET Core and Worker Service packages, applied by theirComponentVersionTelemetryInitializer. - Your own initializer, which is the most predictable when builds come from CI.
A 2.x initializer that sets the version and environment from the build:
public class BuildInfoInitializer : ITelemetryInitializer
{
private static readonly string Version =
typeof(BuildInfoInitializer).Assembly
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "unknown";
private static readonly string Environment =
System.Environment.GetEnvironmentVariable("DEPLOY_ENVIRONMENT") ?? "unknown";
public void Initialize(ITelemetry telemetry)
{
telemetry.Context.Component.Version = Version;
if (telemetry is ISupportProperties p && !p.Properties.ContainsKey("environment"))
p.Properties["environment"] = Environment;
}
}
Read the values once into static fields: initializers run on every telemetry item. The initializers guide covers registration.
Today: SDK 3.x and the Microsoft OpenTelemetry Distro
OpenTelemetry has a standard attribute for this: service.version, a stable resource attribute described as the version string of the service component, in a format the conventions leave to you (2.0.0 and a commit hash are both valid examples). Microsoft’s .NET Azure Monitor exporter has mapped service.version to Application Version for traces, metrics and logs since release 1.3.0 (June 2024), so it lands in application_Version. Microsoft’s migration guidance makes the same mapping explicit for SDK 3.x: role name, role instance and component version become service.name, service.instance.id and service.version.
The simplest way to set it is the environment, which keeps the build number out of code:
export OTEL_SERVICE_NAME="orders-api"
export OTEL_RESOURCE_ATTRIBUTES="service.version=2026.10.21.3,service.namespace=contoso"
In code with the Microsoft OpenTelemetry Distro, read the version from the assembly once and put it on the resource:
var version = Assembly.GetEntryAssembly()?
.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "unknown";
builder.Services.AddOpenTelemetry()
.UseMicrosoftOpenTelemetry(o => o.Exporters = ExportTarget.AzureMonitor)
.ConfigureResource(r => r.AddService(serviceName: "orders-api", serviceVersion: version));
The Node.js and Python distros read the same OTEL_RESOURCE_ATTRIBUTES and OTEL_SERVICE_NAME variables for the role name. The changelog entry that documents the service.version mapping belongs to the .NET exporter, so in another language set the attribute the same way and check a few rows of application_Version before building dashboards on it.
SDK 3.x replaces the 2.x ComponentVersionTelemetryInitializer with a default of Assembly.GetEntryAssembly().GetName().Version, or the ApplicationVersion property if you set it; applicationinsights.config also accepts an <ApplicationVersion> element. Because resource attributes are fixed once the OpenTelemetry SDK is built, the migration guide notes a difference: in apps without dependency injection you set TelemetryClient.Context.Component.Version before sending, while ASP.NET Core and Worker Service apps configure the resource (serviceVersion) through the OpenTelemetry builder.
The assembly version (GetName().Version) is a four-part number; the informational version can carry a full semantic version with build metadata. Pass whichever your pipeline makes unique, for example with dotnet build -p:Version=2026.10.21.3 or -p:InformationalVersion=....
Environment is a different question from version. Microsoft notes that resource-based values don’t necessarily appear on every telemetry item; for a key-value pair on every item, use SDK 3.x’s GlobalProperties or a processor, as in the initializers guide.
Mark deployments on the charts
Release annotations put a marker on the Performance, Failures and Usage charts and on workbook time charts (not in the Metrics pane). Azure Pipelines creates them automatically for App Service and Functions deployment tasks when the target links to Application Insights through APPLICATIONINSIGHTS_CONNECTION_STRING in the same subscription. From anywhere else, Microsoft provides a PowerShell script that calls the Annotations API with the Azure CLI’s token.
The core of Microsoft’s annotation script (Category must be Deployment or the portal won’t show it):
$annotation = @{
Id = [guid]::NewGuid()
AnnotationName = "orders-api 2026.10.21.3"
EventTime = (Get-Date).ToUniversalTime().GetDateTimeFormats("s")[0]
Category = "Deployment"
Properties = (@{ BuildNumber = "2026.10.21.3"; SourceBranch = "main" } | ConvertTo-Json -Compress)
} | ConvertTo-Json -Compress
$token = (az account get-access-token | ConvertFrom-Json).accessToken
Invoke-RestMethod -Method Put -Uri "https://management.azure.com$aiResourceId/Annotations?api-version=2015-05-01" `
-Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } -Body $annotation
Microsoft’s full script also escapes non-ASCII characters in the JSON; use it as is if your release names can contain them.
Annotations and the version column answer the same question from two sides. The annotation shows when a deployment happened; the version shows which telemetry came from which build, which is what matters during a gradual rollout, when old and new versions serve traffic side by side for an hour or a day.
Compare versions in KQL
Failure rate and p95 latency per version, hour by hour, so a bad rollout stands out:
requests
| where timestamp > ago(3d)
| summarize requests = sum(itemCount),
failed = sumif(itemCount, tostring(success) =~ "false"),
p95_ms = percentilew(duration, itemCount, 95)
by application_Version, bin(timestamp, 1h)
| extend failurePct = round(100.0 * failed / requests, 2)
| order by timestamp asc
Rollouts and scaling show up in the same data. Counting instances per version over time separates a deployment (one version’s instances fall as another’s rise) from a scale-out (the same version gains instances):
requests
| where timestamp > ago(1d)
| summarize instances = dcount(cloud_RoleInstance) by application_Version, bin(timestamp, 10m)
| render timechart
The latency by operation query adds application_Version to its by clause just as easily.
Troubleshooting
| Symptom | Likely cause | Fix |
|---|---|---|
application_Version empty (OpenTelemetry) | service.version not set | Set it in OTEL_RESOURCE_ATTRIBUTES or ConfigureResource |
Version shows 1.0.0.0 everywhere | Default assembly version never changed | Pass a version from the pipeline at build time |
| Two deployments share one version | Version not unique per build | Add the build number or commit |
| Annotations missing from charts | Category not Deployment, or looking at Metrics | Set the category; use Performance, Failures or workbooks |
| Environment property missing on some items | Set as a resource attribute only | Use GlobalProperties or a processor |
| 2.x version ignored after upgrading to 3.x | Initializers no longer run | Use ApplicationVersion or service.version |
Queries for this topic
-
Cloud role instances and versions seen per role
requests operations
Returns: One row per service and application version with its sampling-adjusted request count, the number and names of instances that served it, and when it was first and last seen in the window.
-
Top failing operations, this week vs last week
requests failures
Returns: Up to 20 operations with the most failed requests in the last 7 days, each with last week's failures, the change in count and percent, and this week's total requests.
Frequently asked questions
Which version should I report, the semantic version or the build number?
Whichever identifies one deployable build uniquely. Two deployments with the same application_Version can't be told apart in queries, so a semantic version alone isn't enough if you redeploy without bumping it. A version plus build number or commit is the safe choice.
Why is application_Version empty with OpenTelemetry?
Nothing set service.version. Microsoft's .NET exporter maps that resource attribute to the application version; set it with OTEL_RESOURCE_ATTRIBUTES or ConfigureResource. SDK 3.x fills it from the entry assembly's version unless you set ApplicationVersion.
How do I tell environments apart, such as staging and production?
Use a separate Application Insights resource per environment where you can. Inside one resource, add an environment property to every item (an initializer in 2.x, GlobalProperties or a processor today) rather than encoding it in the version string.
Sources, checked
- Application Insights telemetry data model (application context)
- Monitor .NET and Node.js applications with Application Insights (Classic API 2.x, telemetry initializers)
- Migration guidance from 2.x to 3.x (component version and service.version)
- Breaking changes: Application Insights 2.x to 3.x (ApplicationVersion setting)
- Azure.Monitor.OpenTelemetry.Exporter changelog (service.version mapped to Application Version)
- Service attributes (OpenTelemetry semantic conventions)
- Configure OpenTelemetry in Application Insights (resource attributes)
- Investigate failures, performance and transactions (release annotations)