diff --git a/DirectConnector/Configuration/ConnectorOptions.cs b/DirectConnector/Configuration/ConnectorOptions.cs index 4e57886..337793d 100644 --- a/DirectConnector/Configuration/ConnectorOptions.cs +++ b/DirectConnector/Configuration/ConnectorOptions.cs @@ -17,6 +17,18 @@ public class ConnectorOptions /// public const string SectionName = "Connectors"; + /// + /// When true, use for local development. + /// Set to true in local.settings.json for local dev; leave false (default) in Azure. + /// + public bool UseAzureCliCredential { get; set; } + + /// + /// Managed identity client ID for user-assigned identity. + /// Leave unset (null or empty) for system-assigned managed identity (default). + /// + public string? ManagedIdentityClientId { get; set; } + /// /// Office365 connector options. /// @@ -88,13 +100,6 @@ public class TeamsOptions /// [Required(ErrorMessage = "Teams ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -107,13 +112,6 @@ public class Office365Options /// [Required(ErrorMessage = "Office365 ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -126,13 +124,6 @@ public class SharePointOptions /// [Required(ErrorMessage = "SharePoint ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -145,13 +136,6 @@ public class OneDriveOptions /// [Required(ErrorMessage = "OneDrive ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -164,13 +148,6 @@ public class MsGraphOptions /// [Required(ErrorMessage = "Connectors:MsGraph:ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -183,13 +160,6 @@ public class AzureBlobOptions /// [Required(ErrorMessage = "Connectors:AzureBlob:ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -202,13 +172,6 @@ public class SmtpOptions /// [Required(ErrorMessage = "Connectors:Smtp:ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -221,13 +184,6 @@ public class MqOptions /// [Required(ErrorMessage = "Connectors:Mq:ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -240,13 +196,6 @@ public class Office365UsersOptions /// [Required(ErrorMessage = "Connectors:Office365Users:ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } /// @@ -259,11 +208,4 @@ public class AzureLogAnalyticsOptions /// [Required(ErrorMessage = "Connectors:AzureLogAnalytics:ConnectionRuntimeUrl is required.")] public string ConnectionRuntimeUrl { get; set; } = string.Empty; - - /// - /// Managed identity client ID for user-assigned identity. - /// Set to empty string for system-assigned managed identity. - /// Leave unset (null) to use the DefaultAzureCredential chain (CLI, env vars, etc.). - /// - public string? ManagedIdentityClientId { get; set; } } diff --git a/DirectConnector/Program.cs b/DirectConnector/Program.cs index e88984c..0888389 100644 --- a/DirectConnector/Program.cs +++ b/DirectConnector/Program.cs @@ -2,6 +2,8 @@ // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------ +using Azure.Core; +using Azure.Identity; using DirectConnector.Configuration; using Microsoft.Azure.Connectors.DirectClient.Azureblob; using Microsoft.Azure.Connectors.DirectClient.Azureloganalytics; @@ -35,126 +37,111 @@ services.AddHttpClient(); + // NOTE: Resolve the credential once for all connector clients. + // In Azure: defaults to ManagedIdentityCredential (system-assigned). + // For user-assigned MSI: set ManagedIdentityClientId in configuration. + // For local dev: set UseAzureCliCredential=true in local.settings.json. + services.AddSingleton(serviceProvider => + { + var options = serviceProvider.GetRequiredService>().Value; + + if (options.UseAzureCliCredential) + { + return new AzureCliCredential(); + } + + if (!string.IsNullOrEmpty(options.ManagedIdentityClientId)) + { + return new ManagedIdentityCredential( + ManagedIdentityId.FromUserAssignedClientId(options.ManagedIdentityClientId)); + } + + return new ManagedIdentityCredential(ManagedIdentityId.SystemAssigned); + }); + // NOTE: Register generated connector clients as singletons. - // The factory overload lets DI own the instance lifetime and call Dispose, - // which exercises the ownership-based disposal pattern: the client will - // dispose its internally-created HttpClient and DefaultAzureCredential. + // The factory overload lets DI own the instance lifetime and call Dispose. + // All clients share the same credential resolved above. // NOTE: Validation of ConnectionRuntimeUrl is handled by // [Required] attribute + ValidateOnStart() at host initialization. services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - // NOTE: When ManagedIdentityClientId is null, use the default constructor so - // the client relies on DefaultAzureCredential. When ManagedIdentityClientId is non-null - // (empty string = system-assigned MSI, non-empty = user-assigned MSI), use the MSI constructor. - return options.Office365.ManagedIdentityClientId != null - ? new Office365Client( - options.Office365.ConnectionRuntimeUrl, - options.Office365.ManagedIdentityClientId) - : new Office365Client(options.Office365.ConnectionRuntimeUrl); + return new Office365Client(options.Office365.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - // NOTE: When ManagedIdentityClientId is null, use the default constructor so - // the client relies on DefaultAzureCredential. When ManagedIdentityClientId is non-null - // (empty string = system-assigned MSI, non-empty = user-assigned MSI), use the MSI constructor. - return options.SharePoint.ManagedIdentityClientId != null - ? new SharepointonlineClient( - options.SharePoint.ConnectionRuntimeUrl, - options.SharePoint.ManagedIdentityClientId) - : new SharepointonlineClient(options.SharePoint.ConnectionRuntimeUrl); + return new SharepointonlineClient(options.SharePoint.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - return options.Teams.ManagedIdentityClientId != null - ? new TeamsClient( - options.Teams.ConnectionRuntimeUrl, - options.Teams.ManagedIdentityClientId) - : new TeamsClient(options.Teams.ConnectionRuntimeUrl); + return new TeamsClient(options.Teams.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - return options.OneDrive.ManagedIdentityClientId != null - ? new OnedriveforbusinessClient( - options.OneDrive.ConnectionRuntimeUrl, - options.OneDrive.ManagedIdentityClientId) - : new OnedriveforbusinessClient(options.OneDrive.ConnectionRuntimeUrl); + return new OnedriveforbusinessClient(options.OneDrive.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - return options.MsGraph.ManagedIdentityClientId != null - ? new MsgraphgroupsanduserClient( - options.MsGraph.ConnectionRuntimeUrl, - options.MsGraph.ManagedIdentityClientId) - : new MsgraphgroupsanduserClient(options.MsGraph.ConnectionRuntimeUrl); + return new MsgraphgroupsanduserClient(options.MsGraph.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - return options.AzureBlob.ManagedIdentityClientId != null - ? new AzureblobClient( - options.AzureBlob.ConnectionRuntimeUrl, - options.AzureBlob.ManagedIdentityClientId) - : new AzureblobClient(options.AzureBlob.ConnectionRuntimeUrl); + return new AzureblobClient(options.AzureBlob.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - return options.Smtp.ManagedIdentityClientId != null - ? new SmtpClient( - options.Smtp.ConnectionRuntimeUrl, - options.Smtp.ManagedIdentityClientId) - : new SmtpClient(options.Smtp.ConnectionRuntimeUrl); + return new SmtpClient(options.Smtp.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - return options.Mq.ManagedIdentityClientId != null - ? new MqClient( - options.Mq.ConnectionRuntimeUrl, - options.Mq.ManagedIdentityClientId) - : new MqClient(options.Mq.ConnectionRuntimeUrl); + return new MqClient(options.Mq.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - return options.Office365Users.ManagedIdentityClientId != null - ? new Office365usersClient( - options.Office365Users.ConnectionRuntimeUrl, - options.Office365Users.ManagedIdentityClientId) - : new Office365usersClient(options.Office365Users.ConnectionRuntimeUrl); + return new Office365usersClient(options.Office365Users.ConnectionRuntimeUrl, credential); }); services.AddSingleton(serviceProvider => { var options = serviceProvider.GetRequiredService>().Value; + var credential = serviceProvider.GetRequiredService(); - return options.AzureLogAnalytics.ManagedIdentityClientId != null - ? new AzureloganalyticsClient( - options.AzureLogAnalytics.ConnectionRuntimeUrl, - options.AzureLogAnalytics.ManagedIdentityClientId) - : new AzureloganalyticsClient(options.AzureLogAnalytics.ConnectionRuntimeUrl); + return new AzureloganalyticsClient(options.AzureLogAnalytics.ConnectionRuntimeUrl, credential); }); }) .Build();