Skip to content
Last9
Book demo

ASP.NET on IIS (.NET Framework)

Instrument ASP.NET applications on IIS with OpenTelemetry to send traces, metrics, and logs to Last9

Use the OpenTelemetry .NET Automatic Instrumentation agent to instrument ASP.NET applications running on IIS and send telemetry data to Last9. The agent uses the .NET CLR profiler to capture traces from incoming HTTP requests, outbound HTTP calls, SQL queries, and WCF services.

What Gets Auto-Instrumented

LibraryWhat’s captured
ASP.NET / ASP.NET MVCIncoming HTTP request spans with method, URL, status code, duration
System.Net.Http / WebClientOutbound HTTP call spans
ADO.NET (System.Data)SQL query spans with db.statement
WCF (client)Service call spans

Prerequisites

  • Windows Server 2016 or later with IIS 8.5+
  • .NET Framework 4.6.2–4.8 installed
  • Administrator access on the server
  • IIS app pool name — find it with: Get-WebApplication | Select ApplicationPool
  • Last9 Account with OpenTelemetry integration credentials

Installation

  1. Download the auto-instrumentation package

    Open an elevated PowerShell session (Windows PowerShell 5.1, not PowerShell 7):

    [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
    $version = "v1.14.1"
    $otelHome = "C:\Program Files\OpenTelemetry .NET AutoInstrumentation"
    # Download the instrumentation package
    Invoke-WebRequest `
    -Uri "https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/$version/opentelemetry-dotnet-instrumentation-windows.zip" `
    -OutFile "$env:TEMP\otel-dotnet.zip" -UseBasicParsing
    # Download the PowerShell management module
    Invoke-WebRequest `
    -Uri "https://github.com/open-telemetry/opentelemetry-dotnet-instrumentation/releases/download/$version/OpenTelemetry.DotNet.Auto.psm1" `
    -OutFile "$env:TEMP\OpenTelemetry.DotNet.Auto.psm1" -UseBasicParsing
    # Extract
    New-Item -Path $otelHome -ItemType Directory -Force | Out-Null
    Expand-Archive -Path "$env:TEMP\otel-dotnet.zip" -DestinationPath $otelHome -Force
    Copy-Item "$env:TEMP\OpenTelemetry.DotNet.Auto.psm1" "$otelHome\" -Force

    Verify the installation:

    Test-Path "$otelHome\win-x64\OpenTelemetry.AutoInstrumentation.Native.dll"
    # Should return: True
  2. Register the profiler for IIS

    The official PowerShell module registers the CLR profiler and configures IIS globally:

    $env:OTEL_DOTNET_AUTO_HOME = "C:\Program Files\OpenTelemetry .NET AutoInstrumentation"
    Import-Module "$env:OTEL_DOTNET_AUTO_HOME\OpenTelemetry.DotNet.Auto.psm1"
    Install-OpenTelemetryCore
    Register-OpenTelemetryForIIS

    This restarts IIS automatically. The profiler is now enabled for all app pools.

  3. Configure your app pool

    Set environment variables on the specific app pool to control service name, exporter, and sampling:

    Import-Module WebAdministration
    $poolName = "YourAppPool" # Replace with your app pool name
    Set-ItemProperty "IIS:\AppPools\$poolName" -Name environmentVariables -Value @(
    @{ name = 'OTEL_SERVICE_NAME'; value = 'your-service-name' },
    @{ name = 'OTEL_RESOURCE_ATTRIBUTES'; value = "deployment.environment=production,host.name=$env:COMPUTERNAME" },
    @{ name = 'OTEL_TRACES_EXPORTER'; value = 'otlp' },
    @{ name = 'OTEL_METRICS_EXPORTER'; value = 'otlp' },
    @{ name = 'OTEL_LOGS_EXPORTER'; value = 'otlp' },
    @{ name = 'OTEL_EXPORTER_OTLP_ENDPOINT'; value = '$last9_otlp_endpoint' },
    @{ name = 'OTEL_EXPORTER_OTLP_PROTOCOL'; value = 'http/protobuf' },
    @{ name = 'OTEL_EXPORTER_OTLP_HEADERS'; value = 'Authorization=$last9_otlp_auth_header' },
    @{ name = 'OTEL_PROPAGATORS'; value = 'tracecontext,baggage' },
    @{ name = 'OTEL_TRACES_SAMPLER'; value = 'always_on' }
    )
  4. Restart the app pool

    Restart-WebAppPool -Name "YourAppPool"
  5. Verify instrumentation

    Make a request to your application, then verify the profiler loaded:

    Get-Process w3wp | ForEach-Object {
    $modules = $_.Modules | Where-Object { $_.ModuleName -like "*OpenTelemetry*" }
    if ($modules) {
    Write-Host "PID $($_.Id): OTel profiler loaded"
    $modules | Select ModuleName
    }
    }

    You should see OpenTelemetry.AutoInstrumentation.Native in the module list.

    Check Last9 Traces and filter by your service.name to see incoming and outgoing HTTP spans.

Distributed Trace Propagation

The auto-instrumentation automatically reads and writes W3C traceparent headers on all HTTP requests. This means:

  • Incoming requests with a traceparent header (from upstream services, mobile apps, or load balancers) will be continued as part of the same trace
  • Outgoing requests (via HttpClient, WebClient, or HttpWebRequest) will automatically include the traceparent header, propagating the trace to downstream services

No configuration needed — this works by default when OTEL_PROPAGATORS=tracecontext,baggage is set.

Sampling Configuration

SamplerValueUse case
always_onCapture all tracesDevelopment, debugging
always_offCapture no tracesDisable temporarily
traceidratioSample by percentageProduction (set OTEL_TRACES_SAMPLER_ARG=0.1 for 10%)
parentbased_traceidratioRespect parent’s sampling decision, ratio for new tracesProduction with distributed tracing

For production, use parentbased_traceidratio to respect upstream sampling decisions while controlling the sampling rate for traces originating at this service.

Multi-App Pool Configuration

When multiple applications share an app pool, they share the same profiler configuration. To give each application its own service name, configure them in separate app pools with different OTEL_SERVICE_NAME values.

To disable instrumentation for a specific app pool:

Import-Module "$env:OTEL_DOTNET_AUTO_HOME\OpenTelemetry.DotNet.Auto.psm1"
Disable-OpenTelemetryForIISAppPool -AppPoolName "PoolToDisable"

Troubleshooting

Profiler not loading

Check the .NET Runtime event log:

Get-WinEvent -LogName Application -MaxEvents 20 | Where-Object {
$_.ProviderName -eq '.NET Runtime' -and $_.Message -like '*profiler*'
} | Format-List TimeCreated, Message

Common errors:

  • HRESULT: 0x80004005 — Missing Visual C++ Redistributable. Install from Microsoft
  • CoCreateInstance failed — Run Install-OpenTelemetryCore and Register-OpenTelemetryForIIS again

No spans appearing

  1. Verify the app pool has the correct environment variables:

    (Get-ItemProperty "IIS:\AppPools\YourAppPool").environmentVariables.Collection | Select name, value
  2. Check the auto-instrumentation log:

    Get-ChildItem "$env:TEMP" -Filter "otel-dotnet-auto-*" |
    Sort LastWriteTime -Descending | Select -First 1 | Get-Content | Select -Last 30
  3. Ensure the app pool is running in 64-bit mode (32-bit mode uses a different profiler path):

    (Get-ItemProperty "IIS:\AppPools\YourAppPool").enable32BitAppOnWin64
    # Should be False for 64-bit

SQL query spans missing

ADO.NET instrumentation works automatically for System.Data.SqlClient. If using a third-party data access layer, ensure it ultimately uses ADO.NET under the hood.

Need Help?

If you encounter any issues or have questions: