Skip to content
Closed
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
82 changes: 12 additions & 70 deletions DirectConnector/Configuration/ConnectorOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ public class ConnectorOptions
/// </summary>
public const string SectionName = "Connectors";

/// <summary>
/// When true, use <see cref="Azure.Identity.AzureCliCredential"/> for local development.
/// Set to true in local.settings.json for local dev; leave false (default) in Azure.
/// </summary>
public bool UseAzureCliCredential { get; set; }

/// <summary>
/// Managed identity client ID for user-assigned identity.
/// Leave unset (null or empty) for system-assigned managed identity (default).
/// </summary>
public string? ManagedIdentityClientId { get; set; }

Comment thread
daviburg marked this conversation as resolved.
/// <summary>
/// Office365 connector options.
/// </summary>
Expand Down Expand Up @@ -88,13 +100,6 @@ public class TeamsOptions
/// </summary>
[Required(ErrorMessage = "Teams ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -107,13 +112,6 @@ public class Office365Options
/// </summary>
[Required(ErrorMessage = "Office365 ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -126,13 +124,6 @@ public class SharePointOptions
/// </summary>
[Required(ErrorMessage = "SharePoint ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -145,13 +136,6 @@ public class OneDriveOptions
/// </summary>
[Required(ErrorMessage = "OneDrive ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -164,13 +148,6 @@ public class MsGraphOptions
/// </summary>
[Required(ErrorMessage = "Connectors:MsGraph:ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -183,13 +160,6 @@ public class AzureBlobOptions
/// </summary>
[Required(ErrorMessage = "Connectors:AzureBlob:ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -202,13 +172,6 @@ public class SmtpOptions
/// </summary>
[Required(ErrorMessage = "Connectors:Smtp:ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -221,13 +184,6 @@ public class MqOptions
/// </summary>
[Required(ErrorMessage = "Connectors:Mq:ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -240,13 +196,6 @@ public class Office365UsersOptions
/// </summary>
[Required(ErrorMessage = "Connectors:Office365Users:ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}

/// <summary>
Expand All @@ -259,11 +208,4 @@ public class AzureLogAnalyticsOptions
/// </summary>
[Required(ErrorMessage = "Connectors:AzureLogAnalytics:ConnectionRuntimeUrl is required.")]
public string ConnectionRuntimeUrl { get; set; } = string.Empty;

/// <summary>
/// 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.).
/// </summary>
public string? ManagedIdentityClientId { get; set; }
}
105 changes: 46 additions & 59 deletions DirectConnector/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<TokenCredential>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().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<Office365Client>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

// 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<SharepointonlineClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

// 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<TeamsClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

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<OnedriveforbusinessClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

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<MsgraphgroupsanduserClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

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<AzureblobClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

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<SmtpClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

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<MqClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

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<Office365usersClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

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<AzureloganalyticsClient>(serviceProvider =>
{
var options = serviceProvider.GetRequiredService<IOptions<ConnectorOptions>>().Value;
var credential = serviceProvider.GetRequiredService<TokenCredential>();

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();
Expand Down
Loading