Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOp
Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions.ApplicationVersion.set -> void
Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions.ConnectionString.get -> string
Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions.ConnectionString.set -> void
Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions.Credential.get -> Azure.Core.TokenCredential
Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions.Credential.set -> void
Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions.DependencyCollectionOptions.get -> Microsoft.ApplicationInsights.AspNetCore.Extensions.DependencyCollectionOptions
Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions.DeveloperMode.get -> bool?
Microsoft.ApplicationInsights.AspNetCore.Extensions.ApplicationInsightsServiceOptions.DeveloperMode.set -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.Ap
Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.ApplicationVersion.set -> void
Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.ConnectionString.get -> string
Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.ConnectionString.set -> void
Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.Credential.get -> Azure.Core.TokenCredential
Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.Credential.set -> void
Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.DependencyCollectionOptions.get -> Microsoft.ApplicationInsights.WorkerService.DependencyCollectionOptions
Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.DeveloperMode.get -> bool?
Microsoft.ApplicationInsights.WorkerService.ApplicationInsightsServiceOptions.DeveloperMode.set -> void
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.ConnectionStr
Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.DisableTelemetry.get -> bool
Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.DisableTelemetry.set -> void
Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.Dispose() -> void
Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.SetAzureTokenCredential(Azure.Core.TokenCredential tokenCredential) -> void
Microsoft.ApplicationInsights.Extensibility.TelemetryConfiguration.TelemetryConfiguration() -> void
Microsoft.ApplicationInsights.Metric
Microsoft.ApplicationInsights.Metric.TrackValue(double metricValue) -> void
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
namespace Microsoft.ApplicationInsights.Tests
{
using System;
using Azure.Core;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;

/// <summary>
/// Tests for Azure Active Directory (AAD) authentication support in TelemetryConfiguration.
/// </summary>
public class TelemetryConfigurationAadTests : IDisposable
{
private TelemetryConfiguration telemetryConfiguration;

public TelemetryConfigurationAadTests()
{
this.telemetryConfiguration = new TelemetryConfiguration();
}

public void Dispose()
{
this.telemetryConfiguration?.Dispose();
}

[Fact]
public void SetAzureTokenCredential_WithValidCredential_SetsCredentialInExporterOptions()
{
// Arrange
var credential = new MockTokenCredential();
this.telemetryConfiguration.ConnectionString = "InstrumentationKey=test-ikey";

// Act
this.telemetryConfiguration.SetAzureTokenCredential(credential);

// Build the configuration to apply the builder actions
var client = new TelemetryClient(this.telemetryConfiguration);

// Assert - credential should be configured in the service provider
// We can't easily assert on the internal state, but we can verify no exceptions are thrown
Assert.NotNull(client);
}

[Fact]
public void SetAzureTokenCredential_WithNullCredential_ThrowsArgumentNullException()
{
// Act & Assert
Assert.Throws<ArgumentNullException>(() =>
this.telemetryConfiguration.SetAzureTokenCredential(null));
}

[Fact]
public void SetAzureTokenCredential_AfterBuild_ThrowsInvalidOperationException()
{
// Arrange
var credential = new MockTokenCredential();
this.telemetryConfiguration.ConnectionString = "InstrumentationKey=test-ikey";

// Build the configuration
var client = new TelemetryClient(this.telemetryConfiguration);

// Act & Assert
Assert.Throws<InvalidOperationException>(() =>
this.telemetryConfiguration.SetAzureTokenCredential(credential));
}

[Fact]
public void SetAzureTokenCredential_CanBeCalledBeforeBuild_DoesNotThrow()
{
// Arrange
var credential = new MockTokenCredential();
this.telemetryConfiguration.ConnectionString = "InstrumentationKey=test-ikey";

// Act - should not throw
this.telemetryConfiguration.SetAzureTokenCredential(credential);

// Assert
Assert.NotNull(this.telemetryConfiguration);
}

/// <summary>
/// Mock TokenCredential for testing purposes.
/// </summary>
private class MockTokenCredential : TokenCredential
{
public override AccessToken GetToken(TokenRequestContext requestContext, System.Threading.CancellationToken cancellationToken)
{
return new AccessToken("mock-token", DateTimeOffset.UtcNow.AddHours(1));
}

public override System.Threading.Tasks.ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, System.Threading.CancellationToken cancellationToken)
{
return new System.Threading.Tasks.ValueTask<AccessToken>(
new AccessToken("mock-token", DateTimeOffset.UtcNow.AddHours(1)));
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Diagnostics;
using System.Reflection;
using System.Threading;
using Azure.Core;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility.Implementation.Tracing;
using Microsoft.ApplicationInsights.Metrics;
Expand Down Expand Up @@ -156,7 +158,7 @@
{
this.ThrowIfBuilt();

if (configure == null)

Check warning on line 161 in BASE/src/Microsoft.ApplicationInsights/Extensibility/TelemetryConfiguration.cs

View workflow job for this annotation

GitHub Actions / build-test-SHIM (ubuntu-latest, net472)

Use 'ArgumentNullException.ThrowIfNull' instead of explicitly throwing a new exception instance (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1510)
{
throw new ArgumentNullException(nameof(configure));
}
Expand Down Expand Up @@ -186,14 +188,24 @@
/// For more information on expected types, review the documentation for the Azure.Identity library.
/// (https://github.com/Azure/azure-sdk-for-net/tree/master/sdk/identity/Azure.Identity).
/// </remarks>
/// <param name="tokenCredential">An instance of Azure.Core.TokenCredential.</param>
/// <exception cref="ArgumentException">An ArgumentException is thrown if the provided object does not inherit Azure.Core.TokenCredential.</exception>
#pragma warning disable CA1822 // Mark members as static
#pragma warning disable CA1801 // Review unused parameters
internal void SetAzureTokenCredential(object tokenCredential)
#pragma warning restore CA1801 // Review unused parameters
#pragma warning restore CA1822 // Mark members as static
/// <param name="tokenCredential">An instance of TokenCredential.</param>
public void SetAzureTokenCredential(TokenCredential tokenCredential)
{
this.ThrowIfBuilt();

if (tokenCredential == null)
{
throw new ArgumentNullException(nameof(tokenCredential));
}

// Configure the OpenTelemetry builder to pass the credential to Azure Monitor Exporter
this.ConfigureOpenTelemetryBuilder(builder =>
{
builder.Services.Configure<AzureMonitorExporterOptions>(exporterOptions =>
{
exporterOptions.Credential = tokenCredential;
});
});
}

/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## Unreleased
### Added
- [Added support for Entra ID (Azure Active Directory) authentication using Azure.Core.TokenCredential](https://github.com/microsoft/ApplicationInsights-dotnet/pull/3054)

## Version 3.0.0-beta1
- The following Application Insights packages in this repo now use OpenTelemetry internally. OpenTelemetry is the industry standard for telemetry collection and provides better interoperability with other observability tools.
- Microsoft.ApplicationInsights
Expand Down
25 changes: 25 additions & 0 deletions LOGGING/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,31 @@ Application Insights NLog Target nuget package adds ApplicationInsights target i

If your application does not have web.config then it can also be configured manually.

### Azure Active Directory (AAD) Authentication

To use AAD authentication with NLog, configure the TelemetryConfiguration before initializing NLog:

```csharp
using Microsoft.ApplicationInsights.Extensibility;
using Azure.Identity;
using NLog;

// Configure Application Insights with AAD authentication BEFORE NLog initialization
var telemetryConfig = TelemetryConfiguration.CreateDefault();
telemetryConfig.ConnectionString = "InstrumentationKey=YOUR_IKEY;IngestionEndpoint=https://ingestion-endpoint.applicationinsights.azure.com/";
telemetryConfig.SetAzureTokenCredential(new DefaultAzureCredential());

// Now initialize NLog - it will use the configured TelemetryConfiguration
var logger = LogManager.GetCurrentClassLogger();
logger.Info("Using AAD authentication");
```

**Note:** You need to install the `Azure.Identity` NuGet package to use AAD authentication.

For more information, see the [Azure.Identity documentation](https://learn.microsoft.com/dotnet/api/overview/azure/identity-readme).

### Configuration

* **Configure ApplicationInsightsTarget using NLog.config** :

```xml
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,12 @@ internal static IOpenTelemetryBuilder UseApplicationInsightsTelemetry(this IOpen
exporterOptions.ConnectionString = serviceOptions.ConnectionString;
}

// Copy credential to Azure Monitor Exporter
if (serviceOptions.Credential != null)
{
exporterOptions.Credential = serviceOptions.Credential;
}

if (!serviceOptions.EnableAdaptiveSampling)
{
exporterOptions.SamplingRatio = 1.0F;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ internal static IOpenTelemetryBuilder UseApplicationInsightsTelemetry(this IOpen
{
exporterOptions.ConnectionString = serviceOptions.ConnectionString;
}

// Copy credential to Azure Monitor Exporter
if (serviceOptions.Credential != null)
{
exporterOptions.Credential = serviceOptions.Credential;
}

if (!serviceOptions.EnableAdaptiveSampling)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
#if AI_ASPNETCORE_WEB
namespace Microsoft.ApplicationInsights.AspNetCore.Extensions
namespace Microsoft.ApplicationInsights.AspNetCore.Extensions
#else
namespace Microsoft.ApplicationInsights.WorkerService
#endif
{
using System.Reflection;
using Azure.Core;

/// <summary>
/// Application Insights service options defines the custom behavior of the features to add, as opposed to the default selection of features obtained from Application Insights.
Expand Down Expand Up @@ -42,6 +43,13 @@ public class ApplicationInsightsServiceOptions
/// </summary>
public string ConnectionString { get; set; }

/// <summary>
/// Gets or sets the value of <see cref="TokenCredential" />.
/// If <see cref="TokenCredential" /> is not set, AAD authentication is disabled
/// and Instrumentation Key from the Connection String will be used.
/// </summary>
public TokenCredential Credential { get; set; }

/// <summary>
/// Gets or sets the application version reported with telemetries.
/// </summary>
Expand Down Expand Up @@ -124,6 +132,11 @@ internal void CopyPropertiesTo(ApplicationInsightsServiceOptions target)
target.ConnectionString = this.ConnectionString;
}

if (this.Credential != null)
{
target.Credential = this.Credential;
}

target.ApplicationVersion = this.ApplicationVersion;
target.EnableAdaptiveSampling = this.EnableAdaptiveSampling;
target.EnableDebugLogger = this.EnableDebugLogger;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
namespace Microsoft.ApplicationInsights.AspNetCore.Tests.Extensions
{
using System;
using System.Linq;
using Azure.Core;
using Azure.Monitor.OpenTelemetry.Exporter;
using Microsoft.ApplicationInsights.AspNetCore.Extensions;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Xunit;

/// <summary>
/// Integration tests for Azure Active Directory (AAD) authentication support in AspNetCore.
/// </summary>
public class ApplicationInsightsAadIntegrationTests
{
/// <summary>
/// Tests that credential flows from ApplicationInsightsServiceOptions to AzureMonitorExporterOptions.
/// </summary>
[Fact]
public void AddApplicationInsightsTelemetry_WithCredential_FlowsCredentialToExporter()
{
// Arrange
var services = new ServiceCollection();
var credential = new MockTokenCredential();
var connectionString = "InstrumentationKey=test-ikey;IngestionEndpoint=https://test.endpoint.com/";

// Act
services.AddApplicationInsightsTelemetry(options =>
{
options.ConnectionString = connectionString;
options.Credential = credential;
});

var serviceProvider = services.BuildServiceProvider();

// Get the configured AzureMonitorExporterOptions
var exporterOptions = serviceProvider.GetService<IOptions<AzureMonitorExporterOptions>>();

// Assert
Assert.NotNull(exporterOptions);
Assert.NotNull(exporterOptions.Value);
Assert.Same(credential, exporterOptions.Value.Credential);
}

/// <summary>
/// Tests that credential is null when not set.
/// </summary>
[Fact]
public void AddApplicationInsightsTelemetry_WithoutCredential_HasNullCredentialInExporter()
{
// Arrange
var services = new ServiceCollection();
var connectionString = "InstrumentationKey=test-ikey;IngestionEndpoint=https://test.endpoint.com/";

// Act
services.AddApplicationInsightsTelemetry(options =>
{
options.ConnectionString = connectionString;
// No credential set
});

var serviceProvider = services.BuildServiceProvider();

// Get the configured AzureMonitorExporterOptions
var exporterOptions = serviceProvider.GetService<IOptions<AzureMonitorExporterOptions>>();

// Assert
Assert.NotNull(exporterOptions);
Assert.NotNull(exporterOptions.Value);
Assert.Null(exporterOptions.Value.Credential);
}

/// <summary>
/// Tests that TelemetryClient can be created with credential configured.
/// </summary>
[Fact]
public void TelemetryClient_WithCredential_CreatesSuccessfully()
{
// Arrange
var services = new ServiceCollection();
var credential = new MockTokenCredential();
var connectionString = "InstrumentationKey=test-ikey;IngestionEndpoint=https://test.endpoint.com/";

services.AddApplicationInsightsTelemetry(options =>
{
options.ConnectionString = connectionString;
options.Credential = credential;
});

var serviceProvider = services.BuildServiceProvider();

// Act
var telemetryClient = serviceProvider.GetService<TelemetryClient>();

// Assert
Assert.NotNull(telemetryClient);
Assert.NotNull(telemetryClient.TelemetryConfiguration);
}

/// <summary>
/// Mock TokenCredential for testing purposes.
/// </summary>
private class MockTokenCredential : TokenCredential
{
public override AccessToken GetToken(TokenRequestContext requestContext, System.Threading.CancellationToken cancellationToken)
{
return new AccessToken("mock-token", DateTimeOffset.UtcNow.AddHours(1));
}

public override System.Threading.Tasks.ValueTask<AccessToken> GetTokenAsync(TokenRequestContext requestContext, System.Threading.CancellationToken cancellationToken)
{
return new System.Threading.Tasks.ValueTask<AccessToken>(
new AccessToken("mock-token", DateTimeOffset.UtcNow.AddHours(1)));
}
}
}
}
Loading
Loading