From 7a8902a5cefb679bdf3ad8fdc2aa9d00c5fb73a2 Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Sun, 13 Apr 2025 15:15:23 -0400 Subject: [PATCH 1/8] Introduce IAWSCredentialsFactory. Update ClientFactory to use it. --- .../AWSOptions.cs | 7 +- .../ClientFactory.cs | 50 +------------ .../DefaultAWSCredentialsFactory.cs | 73 +++++++++++++++++++ .../IAWSCredentialsFactory.cs | 15 ++++ 4 files changed, 97 insertions(+), 48 deletions(-) create mode 100644 extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentialsFactory.cs create mode 100644 extensions/src/AWSSDK.Extensions.NETCore.Setup/IAWSCredentialsFactory.cs diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs index e137c66fd175..3b20c866457d 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs @@ -13,7 +13,7 @@ * permissions and limitations under the License. */ using Amazon.Runtime; - +using AWSSDK.Extensions.NETCore.Setup; using Microsoft.Extensions.Logging; namespace Amazon.Extensions.NETCore.Setup @@ -55,6 +55,11 @@ public class AWSOptions /// public string ExternalId { get; set; } + /// + /// + /// + public IAWSCredentialsFactory CredentialsFactory { get; set; } + /// /// AWS Credentials used for creating service clients. If this is set it overrides the Profile property. /// diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs index 26c402eada88..d1f84c78d2cb 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs @@ -21,6 +21,7 @@ using Amazon.Runtime; using Amazon.Runtime.CredentialManagement; using Amazon.Runtime.Credentials.Internal; +using AWSSDK.Extensions.NETCore.Setup; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -83,7 +84,8 @@ internal object CreateServiceClient(IServiceProvider provider) internal IAmazonService CreateServiceClient(ILogger logger, AWSOptions options) { PerformGlobalConfig(logger, options); - var credentials = CreateCredentials(logger, options); + var credentialsFactory = options.CredentialsFactory ?? new DefaultAWSCredentialsFactory(options, logger); + var credentials = credentialsFactory.Create(); if (!string.IsNullOrEmpty(options?.SessionRoleArn)) { @@ -165,52 +167,6 @@ private static AmazonServiceClient CreateClient(AWSCredentials credentials, Clie #endif } - /// - /// Creates the AWSCredentials using either the profile indicated from the AWSOptions object - /// of the SDK fallback credentials search. - /// - /// - /// - /// - private static AWSCredentials CreateCredentials(ILogger logger, AWSOptions options) - { - if (options != null) - { - if (options.Credentials != null) - { - logger?.LogInformation("Using AWS credentials specified with the AWSOptions.Credentials property"); - return options.Credentials; - } - if (!string.IsNullOrEmpty(options.Profile)) - { - var chain = new CredentialProfileStoreChain(options.ProfilesLocation); - AWSCredentials result; - if (chain.TryGetAWSCredentials(options.Profile, out result)) - { - logger?.LogInformation($"Found AWS credentials for the profile {options.Profile}"); - return result; - } - else - { - logger?.LogInformation($"Failed to find AWS credentials for the profile {options.Profile}"); - } - } - } - - var credentials = DefaultIdentityResolverConfiguration.ResolveDefaultIdentity(); - if (credentials == null) - { - logger?.LogError("Last effort to find AWS Credentials with AWS SDK's default credential search failed"); - throw new AmazonClientException("Failed to find AWS Credentials for constructing AWS service client"); - } - else - { - logger?.LogInformation("Found credentials using the AWS SDK's default credential search"); - } - - return credentials; - } - /// /// Creates the ClientConfig object for the service client. /// diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentialsFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentialsFactory.cs new file mode 100644 index 000000000000..0d2c88f74e5b --- /dev/null +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentialsFactory.cs @@ -0,0 +1,73 @@ +using Amazon.Extensions.NETCore.Setup; +using Amazon.Runtime; +using Amazon.Runtime.CredentialManagement; +using Amazon.Runtime.Credentials.Internal; +using Microsoft.Extensions.Logging; + +namespace AWSSDK.Extensions.NETCore.Setup +{ + /// + /// + /// + public class DefaultAWSCredentialsFactory : IAWSCredentialsFactory + { + private readonly AWSOptions _options; + private readonly ILogger _logger; + + /// + /// Creates the AWSCredentials using either the profile indicated from the AWSOptions object + /// of the SDK fallback credentials search. + /// + /// + /// + /// + public DefaultAWSCredentialsFactory(AWSOptions options, ILogger logger = null) + { + _options = options; + _logger = logger; + } + + /// + /// Creates the AWSCredentials using either AWSOptions.Credentials, AWSOptions.Profile + AWSOptions.ProfilesLocation, + /// or the SDK fallback credentials search. + /// + public AWSCredentials Create() + { + if (_options != null) + { + if (_options.Credentials != null) + { + _logger?.LogInformation("Using AWS credentials specified with the AWSOptions.Credentials property"); + return _options.Credentials; + } + if (!string.IsNullOrEmpty(_options.Profile)) + { + var chain = new CredentialProfileStoreChain(_options.ProfilesLocation); + AWSCredentials result; + if (chain.TryGetAWSCredentials(_options.Profile, out result)) + { + _logger?.LogInformation($"Found AWS credentials for the profile {_options.Profile}"); + return result; + } + else + { + _logger?.LogInformation($"Failed to find AWS credentials for the profile {_options.Profile}"); + } + } + } + + var credentials = DefaultIdentityResolverConfiguration.ResolveDefaultIdentity(); + if (credentials == null) + { + _logger?.LogError("Last effort to find AWS Credentials with AWS SDK's default credential search failed"); + throw new AmazonClientException("Failed to find AWS Credentials for constructing AWS service client"); + } + else + { + _logger?.LogInformation("Found credentials using the AWS SDK's default credential search"); + } + + return credentials; + } + } +} \ No newline at end of file diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/IAWSCredentialsFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/IAWSCredentialsFactory.cs new file mode 100644 index 000000000000..682b1015f3c8 --- /dev/null +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/IAWSCredentialsFactory.cs @@ -0,0 +1,15 @@ +using Amazon.Runtime; + +namespace AWSSDK.Extensions.NETCore.Setup +{ + /// + /// + /// + public interface IAWSCredentialsFactory + { + /// + /// Creates AWSCredentials + /// + AWSCredentials Create(); + } +} \ No newline at end of file From 63674cd3a116d1c24c4027b43d7c8cf361580192 Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 09:05:53 -0400 Subject: [PATCH 2/8] Expose [Try]AddAWSCredentialsFactory methods --- .../AWSOptions.cs | 2 +- .../ClientFactory.cs | 60 ++++--------- .../ServiceCollectionExtensions.cs | 89 ++++++++++++++++--- 3 files changed, 97 insertions(+), 54 deletions(-) diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs index 3b20c866457d..dbf4d2feb1dd 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs @@ -106,7 +106,7 @@ internal set /// The service client that implements the service interface. public T CreateServiceClient() where T : class, IAmazonService { - return new ClientFactory(this).CreateServiceClient((ILogger)null, this) as T; + return new ClientFactory(this, CredentialsFactory, (ILogger)null).CreateServiceClient() as T; } /// diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs index d1f84c78d2cb..47bd3f1666d3 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs @@ -37,69 +37,45 @@ internal class ClientFactory private static readonly Type[] EMPTY_TYPES = Array.Empty(); private static readonly object[] EMPTY_PARAMETERS = Array.Empty(); - private AWSOptions _awsOptions; + private readonly AWSOptions _options; + private readonly IAWSCredentialsFactory _credentialsFactory; + private readonly ILogger _logger; /// /// Constructs an instance of the ClientFactory /// /// The AWS options used for creating service clients. - internal ClientFactory(AWSOptions awsOptions) - { - _awsOptions = awsOptions; - } - - /// - /// Creates the AWS service client that implements the service client interface. The AWSOptions object - /// will be searched for in the IServiceProvider. - /// - /// The dependency injection provider. - /// The AWS service client - internal object CreateServiceClient(IServiceProvider provider) + /// + /// + internal ClientFactory(AWSOptions awsOptions, IAWSCredentialsFactory credentialsFactory, ILogger logger) { - var loggerFactory = provider.GetService(); - var logger = loggerFactory?.CreateLogger("AWSSDK"); - - var options = _awsOptions ?? provider.GetService(); - if(options == null) - { - var configuration = provider.GetService(); - if(configuration != null) - { - options = configuration.GetAWSOptions(); - if (options != null) - logger?.LogInformation("Found AWS options in IConfiguration"); - } - } - - return CreateServiceClient(logger, options); + _options = awsOptions ?? throw new ArgumentNullException(nameof(awsOptions)); + _credentialsFactory = credentialsFactory ?? throw new ArgumentNullException(nameof(credentialsFactory)); + _logger = logger; } /// - /// Creates the AWS service client that implements the service client interface. The AWSOptions object - /// will be searched for in the IServiceProvider. + /// Creates the AWS service client that implements the service client interface. /// - /// Logger instance for writing diagnostic logs. - /// The AWS options used for creating the service client. /// The AWS service client - internal IAmazonService CreateServiceClient(ILogger logger, AWSOptions options) + internal IAmazonService CreateServiceClient() { - PerformGlobalConfig(logger, options); - var credentialsFactory = options.CredentialsFactory ?? new DefaultAWSCredentialsFactory(options, logger); - var credentials = credentialsFactory.Create(); + PerformGlobalConfig(_logger, _options); + var credentials = _credentialsFactory.Create(); - if (!string.IsNullOrEmpty(options?.SessionRoleArn)) + if (!string.IsNullOrEmpty(_options?.SessionRoleArn)) { - if (string.IsNullOrEmpty(options?.ExternalId)) + if (string.IsNullOrEmpty(_options?.ExternalId)) { - credentials = new AssumeRoleAWSCredentials(credentials, options.SessionRoleArn, options.SessionName); + credentials = new AssumeRoleAWSCredentials(credentials, _options.SessionRoleArn, _options.SessionName); } else { - credentials = new AssumeRoleAWSCredentials(credentials, options.SessionRoleArn, options.SessionName, new AssumeRoleAWSCredentialsOptions() { ExternalId = options.ExternalId }); + credentials = new AssumeRoleAWSCredentials(credentials, _options.SessionRoleArn, _options.SessionName, new AssumeRoleAWSCredentialsOptions() { ExternalId = _options.ExternalId }); } } - var config = CreateConfig(options); + var config = CreateConfig(_options); var client = CreateClient(credentials, config); return client as IAmazonService; } diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index c0cf329b5104..959cf9639e59 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -21,7 +21,9 @@ using Amazon.Runtime; using Amazon.Extensions.NETCore.Setup; +using AWSSDK.Extensions.NETCore.Setup; using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Logging; namespace Microsoft.Extensions.DependencyInjection { @@ -63,6 +65,66 @@ public static IServiceCollection AddDefaultAWSOptions( return collection; } + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentialsFactory( + this IServiceCollection collection, + Func implementationFactory, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), implementationFactory, lifetime)); + return collection; + } + + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentialsFactory( + this IServiceCollection collection, + IAWSCredentialsFactory credentials) + { + collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), credentials)); + return collection; + } + + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentialsFactory( + this IServiceCollection collection, + Func implementationFactory, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), implementationFactory, lifetime)); + return collection; + } + + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentialsFactory( + this IServiceCollection collection, + IAWSCredentialsFactory credentials) + { + collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), credentials)); + return collection; + } + /// /// Adds the AWSOptions object to the dependency injection framework providing information /// that will be used to construct Amazon service clients if they haven't already been registered. @@ -121,10 +183,7 @@ public static IServiceCollection AddAWSService(this IServiceCollection collec /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddAWSService(this IServiceCollection collection, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - Func factory = - new ClientFactory(options).CreateServiceClient; - - var descriptor = new ServiceDescriptor(typeof(T), factory, lifetime); + var descriptor = new ServiceDescriptor(typeof(T), sp => CreateServiceClient(options, sp), lifetime); collection.Add(descriptor); return collection; } @@ -155,14 +214,11 @@ public static IServiceCollection TryAddAWSService(this IServiceCollection col /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddAWSService(this IServiceCollection collection, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - Func factory = - new ClientFactory(options).CreateServiceClient; - - var descriptor = new ServiceDescriptor(typeof(T), factory, lifetime); + var descriptor = new ServiceDescriptor(typeof(T), sp => CreateServiceClient(options, sp), lifetime); collection.TryAdd(descriptor); return collection; } - + #if NET8_0_OR_GREATER /// @@ -193,7 +249,7 @@ public static IServiceCollection AddKeyedAWSService(this IServiceCollection c /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddKeyedAWSService(this IServiceCollection collection, object serviceKey, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - object Factory(IServiceProvider sp, object key) => new ClientFactory(options).CreateServiceClient(sp); + object Factory(IServiceProvider sp, object key) => new ClientFactory(options, sp.GetService(), sp.GetService()).CreateServiceClient(); var descriptor = new ServiceDescriptor(typeof(T), serviceKey, Factory, lifetime); collection.Add(descriptor); @@ -228,12 +284,23 @@ public static IServiceCollection TryAddKeyedAWSService(this IServiceCollectio /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddKeyedAWSService(this IServiceCollection collection, object serviceKey, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - object Factory(IServiceProvider sp, object key) => new ClientFactory(options).CreateServiceClient(sp); + object Factory(IServiceProvider sp, object key) => new ClientFactory(options, sp.GetService(), sp.GetService()).CreateServiceClient(); var descriptor = new ServiceDescriptor(typeof(T), serviceKey, Factory, lifetime); collection.TryAdd(descriptor); return collection; } #endif + + private static object CreateServiceClient(AWSOptions options, IServiceProvider sp) where T : IAmazonService + { + var logger = sp.GetService(); + var awsOptions = options ?? sp.GetService() ?? new AWSOptions(); + var credentialsFactory = awsOptions.CredentialsFactory ?? sp.GetService() ?? new DefaultAWSCredentialsFactory(awsOptions, logger); + + var factory = new ClientFactory(awsOptions, credentialsFactory, logger); + + return factory.CreateServiceClient(); + } } } From 5990d5c32a2efab8e31e5bed3d186dbbeab88a22 Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 11:02:57 -0400 Subject: [PATCH 3/8] Missed some spots --- .../ServiceCollectionExtensions.cs | 42 ++++++++++++++++--- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index 959cf9639e59..1808316a2359 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -65,6 +65,20 @@ public static IServiceCollection AddDefaultAWSOptions( return collection; } + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentialsFactory( + this IServiceCollection collection, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), CreateDefaultCredentialsFactory, lifetime)); + return collection; + } + /// /// /// @@ -95,6 +109,20 @@ public static IServiceCollection AddAWSCredentialsFactory( return collection; } + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentialsFactory( + this IServiceCollection collection, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), CreateDefaultCredentialsFactory, lifetime)); + return collection; + } + /// /// /// @@ -249,9 +277,7 @@ public static IServiceCollection AddKeyedAWSService(this IServiceCollection c /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddKeyedAWSService(this IServiceCollection collection, object serviceKey, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - object Factory(IServiceProvider sp, object key) => new ClientFactory(options, sp.GetService(), sp.GetService()).CreateServiceClient(); - - var descriptor = new ServiceDescriptor(typeof(T), serviceKey, Factory, lifetime); + var descriptor = new ServiceDescriptor(typeof(T), serviceKey, (sp, _) => CreateServiceClient(options, sp), lifetime); collection.Add(descriptor); return collection; } @@ -284,9 +310,7 @@ public static IServiceCollection TryAddKeyedAWSService(this IServiceCollectio /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddKeyedAWSService(this IServiceCollection collection, object serviceKey, AWSOptions options, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - object Factory(IServiceProvider sp, object key) => new ClientFactory(options, sp.GetService(), sp.GetService()).CreateServiceClient(); - - var descriptor = new ServiceDescriptor(typeof(T), serviceKey, Factory, lifetime); + var descriptor = new ServiceDescriptor(typeof(T), serviceKey, (sp, _) => CreateServiceClient(options, sp), lifetime); collection.TryAdd(descriptor); return collection; } @@ -302,5 +326,11 @@ private static object CreateServiceClient(AWSOptions options, IServiceProvide return factory.CreateServiceClient(); } + + private static IAWSCredentialsFactory CreateDefaultCredentialsFactory(IServiceProvider sp) + { + var options = sp.GetService() ?? new AWSOptions(); + return new DefaultAWSCredentialsFactory(options, sp.GetService()); + } } } From 804c1c99a0bfb4cb5d07dc514938fcad0fd9e87c Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 09:34:53 -0400 Subject: [PATCH 4/8] Make AWSOptions a DTO and Add [Try]Add[Keyed]AWSService methods that take an optionsFunc arg --- .../AWSOptions.cs | 17 ---- .../AWSOptionsExtensions.cs | 27 +++++++ .../ServiceCollectionExtensions.cs | 80 +++++++++++++++++-- .../NETCore.SetupTests/ConfigurationTests.cs | 1 + 4 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs index dbf4d2feb1dd..21e9e2611f75 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs @@ -55,11 +55,6 @@ public class AWSOptions /// public string ExternalId { get; set; } - /// - /// - /// - public IAWSCredentialsFactory CredentialsFactory { get; set; } - /// /// AWS Credentials used for creating service clients. If this is set it overrides the Profile property. /// @@ -97,18 +92,6 @@ internal set /// public LoggingSetting Logging { get; set; } - /// - /// Create a service client for the specified service interface using the options set in this instance. - /// For example if T is set to IAmazonS3 then the AmazonS3ServiceClient which implements IAmazonS3 is created - /// and returned. - /// - /// The service interface that a service client will be created for. - /// The service client that implements the service interface. - public T CreateServiceClient() where T : class, IAmazonService - { - return new ClientFactory(this, CredentialsFactory, (ILogger)null).CreateServiceClient() as T; - } - /// /// Container for logging settings of the SDK /// diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs new file mode 100644 index 000000000000..29225771e992 --- /dev/null +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs @@ -0,0 +1,27 @@ +using Amazon.Extensions.NETCore.Setup; +using Amazon.Runtime; + +namespace AWSSDK.Extensions.NETCore.Setup +{ + /// + /// + /// + public static class AWSOptionsExtensions + { + /// + /// Create a service client for the specified service interface using the options set in this instance. + /// For example if T is set to IAmazonS3 then the AmazonS3ServiceClient which implements IAmazonS3 is created + /// and returned. + /// + /// The service interface that a service client will be created for. + /// The service client that implements the service interface. + public static T CreateServiceClient(this AWSOptions options, IAWSCredentialsFactory credentialsFactory = null) + where T : class, IAmazonService + { + credentialsFactory = credentialsFactory ?? new DefaultAWSCredentialsFactory(options); + var clientFactory = new ClientFactory(options, credentialsFactory, null); + + return clientFactory.CreateServiceClient() as T; + } + } +} \ No newline at end of file diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index 1808316a2359..4044dfe4bba5 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -196,7 +196,7 @@ public static IServiceCollection TryAddDefaultAWSOptions( /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddAWSService(this IServiceCollection collection, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - return AddAWSService(collection, null, lifetime); + return AddAWSService(collection, options: null, lifetime); } /// @@ -216,6 +216,23 @@ public static IServiceCollection AddAWSService(this IServiceCollection collec return collection; } + /// + /// Adds the Amazon service client to the dependency injection framework. The Amazon service client is not + /// created until it is requested. If the ServiceLifetime property is set to Singleton, the default, then the same + /// instance will be reused for the lifetime of the process and the object should not be disposed. + /// + /// The AWS service interface, like IAmazonS3. + /// + /// A func that resolves the AWS options used to create the service client overriding the default AWS options added using AddDefaultAWSOptions. + /// The lifetime of the service client created. The default is Singleton. + /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. + public static IServiceCollection AddAWSService(this IServiceCollection collection, Func optionsFunc, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService + { + var descriptor = new ServiceDescriptor(typeof(T), sp => CreateServiceClient(optionsFunc(sp), sp), lifetime); + collection.Add(descriptor); + return collection; + } + /// /// Adds the Amazon service client to the dependency injection framework if the service type hasn't already been registered. /// The Amazon service client is not created until it is requested. If the ServiceLifetime property is set to Singleton, @@ -227,7 +244,7 @@ public static IServiceCollection AddAWSService(this IServiceCollection collec /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddAWSService(this IServiceCollection collection, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - return TryAddAWSService(collection, null, lifetime); + return TryAddAWSService(collection, options: null, lifetime); } /// @@ -247,6 +264,23 @@ public static IServiceCollection TryAddAWSService(this IServiceCollection col return collection; } + /// + /// Adds the Amazon service client to the dependency injection framework if the service type hasn't already been registered. + /// The Amazon service client is not created until it is requested. If the ServiceLifetime property is set to Singleton, + /// the default, then the same instance will be reused for the lifetime of the process and the object should not be disposed. + /// + /// The AWS service interface, like IAmazonS3. + /// + /// A func that resolves the AWS options used to create the service client overriding the default AWS options added using AddDefaultAWSOptions. + /// The lifetime of the service client created. The default is Singleton. + /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. + public static IServiceCollection TryAddAWSService(this IServiceCollection collection, Func optionsFunc, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService + { + var descriptor = new ServiceDescriptor(typeof(T), sp => CreateServiceClient(optionsFunc(sp), sp), lifetime); + collection.TryAdd(descriptor); + return collection; + } + #if NET8_0_OR_GREATER /// @@ -261,7 +295,7 @@ public static IServiceCollection TryAddAWSService(this IServiceCollection col /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddKeyedAWSService(this IServiceCollection collection, object serviceKey, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - return AddKeyedAWSService(collection, serviceKey, null, lifetime); + return AddKeyedAWSService(collection, serviceKey, options: null, lifetime); } /// @@ -281,6 +315,24 @@ public static IServiceCollection AddKeyedAWSService(this IServiceCollection c collection.Add(descriptor); return collection; } + + /// + /// Adds the Amazon service client to the dependency injection framework with a key. The Amazon service client is not + /// created until it is requested. If the ServiceLifetime property is set to Singleton, the default, then the same + /// instance will be reused for the lifetime of the process and the object should not be disposed. + /// + /// The AWS service interface, like IAmazonS3 + /// + /// The key with which the service will be added in the dependency injection framework. + /// A func that resolves the AWS options used to create the service client overriding the default AWS options added using AddDefaultAWSOptions. + /// The lifetime of the service client created. The default is Singleton. + /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. + public static IServiceCollection AddKeyedAWSService(this IServiceCollection collection, object serviceKey, Func optionsFunc, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService + { + var descriptor = new ServiceDescriptor(typeof(T), serviceKey, (sp, _) => CreateServiceClient(optionsFunc(sp), sp), lifetime); + collection.Add(descriptor); + return collection; + } /// /// Adds the Amazon service client to the dependency injection framework with a key if the service type hasn't already been registered with the same key. @@ -294,7 +346,7 @@ public static IServiceCollection AddKeyedAWSService(this IServiceCollection c /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddKeyedAWSService(this IServiceCollection collection, object serviceKey, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - return TryAddKeyedAWSService(collection, serviceKey, null, lifetime); + return TryAddKeyedAWSService(collection, serviceKey, options: null, lifetime); } /// @@ -314,13 +366,31 @@ public static IServiceCollection TryAddKeyedAWSService(this IServiceCollectio collection.TryAdd(descriptor); return collection; } + + /// + /// Adds the Amazon service client to the dependency injection framework with a key if the service type hasn't already been registered with the same key. + /// The Amazon service client is not created until it is requested. If the ServiceLifetime property is set to Singleton, the default, then the same + /// instance will be reused for the lifetime of the process and the object should not be disposed. + /// + /// The AWS service interface, like IAmazonS3 + /// + /// The key with which the service will be added in the dependency injection framework. + /// A func that resolves the AWS options used to create the service client overriding the default AWS options added using AddDefaultAWSOptions. + /// The lifetime of the service client created. The default is Singleton. + /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. + public static IServiceCollection TryAddKeyedAWSService(this IServiceCollection collection, object serviceKey, Func optionsFunc, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService + { + var descriptor = new ServiceDescriptor(typeof(T), serviceKey, (sp, _) => CreateServiceClient(optionsFunc(sp), sp), lifetime); + collection.TryAdd(descriptor); + return collection; + } #endif private static object CreateServiceClient(AWSOptions options, IServiceProvider sp) where T : IAmazonService { var logger = sp.GetService(); var awsOptions = options ?? sp.GetService() ?? new AWSOptions(); - var credentialsFactory = awsOptions.CredentialsFactory ?? sp.GetService() ?? new DefaultAWSCredentialsFactory(awsOptions, logger); + var credentialsFactory = sp.GetService() ?? new DefaultAWSCredentialsFactory(awsOptions, logger); var factory = new ClientFactory(awsOptions, credentialsFactory, logger); diff --git a/extensions/test/NETCore.SetupTests/ConfigurationTests.cs b/extensions/test/NETCore.SetupTests/ConfigurationTests.cs index 731e5f2edefb..cf7aeaa97d25 100644 --- a/extensions/test/NETCore.SetupTests/ConfigurationTests.cs +++ b/extensions/test/NETCore.SetupTests/ConfigurationTests.cs @@ -10,6 +10,7 @@ using Amazon; using Amazon.S3; using Amazon.Runtime; +using AWSSDK.Extensions.NETCore.Setup; namespace NETCore.SetupTests { From 75236390bbab46c05bfff786a69d9270a471f7c1 Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 12:32:00 -0400 Subject: [PATCH 5/8] Expose [Try]AddAWSCredentials extension methods --- .../ClientFactory.cs | 4 +- .../ServiceCollectionExtensions.cs | 98 ++++++++++++++++++- 2 files changed, 98 insertions(+), 4 deletions(-) diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs index 47bd3f1666d3..cb053116150d 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs @@ -58,10 +58,10 @@ internal ClientFactory(AWSOptions awsOptions, IAWSCredentialsFactory credentials /// Creates the AWS service client that implements the service client interface. /// /// The AWS service client - internal IAmazonService CreateServiceClient() + internal IAmazonService CreateServiceClient(AWSCredentials credentials = null) { PerformGlobalConfig(_logger, _options); - var credentials = _credentialsFactory.Create(); + credentials = credentials ?? _credentialsFactory.Create(); if (!string.IsNullOrEmpty(_options?.SessionRoleArn)) { diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index 4044dfe4bba5..5750ce9f7d8a 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -65,6 +65,94 @@ public static IServiceCollection AddDefaultAWSOptions( return collection; } + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentials( + this IServiceCollection collection, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.Add(new ServiceDescriptor(typeof(AWSCredentials), sp => sp.CreateDefaultAWSCredentials(), lifetime)); + return collection; + } + + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentials( + this IServiceCollection collection, + AWSCredentials credentials) + { + collection.Add(new ServiceDescriptor(typeof(AWSCredentials), credentials)); + return collection; + } + + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection AddAWSCredentials( + this IServiceCollection collection, + Func credentialsFunc, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.Add(new ServiceDescriptor(typeof(AWSCredentials), credentialsFunc, lifetime)); + return collection; + } + + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentials( + this IServiceCollection collection, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.TryAdd(new ServiceDescriptor(typeof(AWSCredentials), sp => sp.CreateDefaultAWSCredentials(), lifetime)); + return collection; + } + + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentials( + this IServiceCollection collection, + AWSCredentials credentials) + { + collection.TryAdd(new ServiceDescriptor(typeof(AWSCredentials), credentials)); + return collection; + } + + /// + /// + /// + /// + /// + /// + /// + public static IServiceCollection TryAddAWSCredentials( + this IServiceCollection collection, + Func credentialsFunc, + ServiceLifetime lifetime = ServiceLifetime.Singleton) + { + collection.TryAdd(new ServiceDescriptor(typeof(AWSCredentials), credentialsFunc, lifetime)); + return collection; + } + /// /// /// @@ -394,13 +482,19 @@ private static object CreateServiceClient(AWSOptions options, IServiceProvide var factory = new ClientFactory(awsOptions, credentialsFactory, logger); - return factory.CreateServiceClient(); + return factory.CreateServiceClient(sp.GetService()); } - private static IAWSCredentialsFactory CreateDefaultCredentialsFactory(IServiceProvider sp) + private static IAWSCredentialsFactory CreateDefaultCredentialsFactory(this IServiceProvider sp) { var options = sp.GetService() ?? new AWSOptions(); return new DefaultAWSCredentialsFactory(options, sp.GetService()); } + + private static AWSCredentials CreateDefaultAWSCredentials(this IServiceProvider sp) + { + var credentialsFactory = sp.GetService() ?? sp.CreateDefaultCredentialsFactory(); + return credentialsFactory.Create(); + } } } From 3101a098b3f740873497dbbfc1b487c73fbc087c Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 13:39:06 -0400 Subject: [PATCH 6/8] Remove IAWSCredentialsFactory in favor of DefaultAWSCredentials --- .../AWSOptionsExtensions.cs | 6 +- .../ClientFactory.cs | 12 +- ...alsFactory.cs => DefaultAWSCredentials.cs} | 24 ++-- .../IAWSCredentialsFactory.cs | 15 --- .../ServiceCollectionExtensions.cs | 104 +----------------- 5 files changed, 25 insertions(+), 136 deletions(-) rename extensions/src/AWSSDK.Extensions.NETCore.Setup/{DefaultAWSCredentialsFactory.cs => DefaultAWSCredentials.cs} (74%) delete mode 100644 extensions/src/AWSSDK.Extensions.NETCore.Setup/IAWSCredentialsFactory.cs diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs index 29225771e992..d7f4341f61f0 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs @@ -15,11 +15,11 @@ public static class AWSOptionsExtensions /// /// The service interface that a service client will be created for. /// The service client that implements the service interface. - public static T CreateServiceClient(this AWSOptions options, IAWSCredentialsFactory credentialsFactory = null) + public static T CreateServiceClient(this AWSOptions options) where T : class, IAmazonService { - credentialsFactory = credentialsFactory ?? new DefaultAWSCredentialsFactory(options); - var clientFactory = new ClientFactory(options, credentialsFactory, null); + var credentials = new DefaultAWSCredentials(options, null); + var clientFactory = new ClientFactory(options, credentials, null); return clientFactory.CreateServiceClient() as T; } diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs index cb053116150d..503fbee761c5 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs @@ -38,19 +38,19 @@ internal class ClientFactory private static readonly object[] EMPTY_PARAMETERS = Array.Empty(); private readonly AWSOptions _options; - private readonly IAWSCredentialsFactory _credentialsFactory; + private readonly AWSCredentials _credentials; private readonly ILogger _logger; /// /// Constructs an instance of the ClientFactory /// /// The AWS options used for creating service clients. - /// + /// /// - internal ClientFactory(AWSOptions awsOptions, IAWSCredentialsFactory credentialsFactory, ILogger logger) + internal ClientFactory(AWSOptions awsOptions, AWSCredentials credentials, ILogger logger) { _options = awsOptions ?? throw new ArgumentNullException(nameof(awsOptions)); - _credentialsFactory = credentialsFactory ?? throw new ArgumentNullException(nameof(credentialsFactory)); + _credentials = credentials ?? throw new ArgumentNullException(nameof(credentials)); _logger = logger; } @@ -58,10 +58,10 @@ internal ClientFactory(AWSOptions awsOptions, IAWSCredentialsFactory credentials /// Creates the AWS service client that implements the service client interface. /// /// The AWS service client - internal IAmazonService CreateServiceClient(AWSCredentials credentials = null) + internal IAmazonService CreateServiceClient() { PerformGlobalConfig(_logger, _options); - credentials = credentials ?? _credentialsFactory.Create(); + var credentials = _credentials; if (!string.IsNullOrEmpty(_options?.SessionRoleArn)) { diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentialsFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentials.cs similarity index 74% rename from extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentialsFactory.cs rename to extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentials.cs index 0d2c88f74e5b..7f7439187535 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentialsFactory.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/DefaultAWSCredentials.cs @@ -9,36 +9,34 @@ namespace AWSSDK.Extensions.NETCore.Setup /// /// /// - public class DefaultAWSCredentialsFactory : IAWSCredentialsFactory + public class DefaultAWSCredentials : AWSCredentials { private readonly AWSOptions _options; private readonly ILogger _logger; /// - /// Creates the AWSCredentials using either the profile indicated from the AWSOptions object - /// of the SDK fallback credentials search. + /// /// + /// /// - /// - /// - public DefaultAWSCredentialsFactory(AWSOptions options, ILogger logger = null) + public DefaultAWSCredentials(AWSOptions awsOptions, ILogger logger) { - _options = options; + _options = awsOptions; _logger = logger; } /// - /// Creates the AWSCredentials using either AWSOptions.Credentials, AWSOptions.Profile + AWSOptions.ProfilesLocation, - /// or the SDK fallback credentials search. + /// /// - public AWSCredentials Create() + /// + public override ImmutableCredentials GetCredentials() { if (_options != null) { if (_options.Credentials != null) { _logger?.LogInformation("Using AWS credentials specified with the AWSOptions.Credentials property"); - return _options.Credentials; + return _options.Credentials.GetCredentials(); } if (!string.IsNullOrEmpty(_options.Profile)) { @@ -47,7 +45,7 @@ public AWSCredentials Create() if (chain.TryGetAWSCredentials(_options.Profile, out result)) { _logger?.LogInformation($"Found AWS credentials for the profile {_options.Profile}"); - return result; + return result.GetCredentials(); } else { @@ -67,7 +65,7 @@ public AWSCredentials Create() _logger?.LogInformation("Found credentials using the AWS SDK's default credential search"); } - return credentials; + return credentials.GetCredentials(); } } } \ No newline at end of file diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/IAWSCredentialsFactory.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/IAWSCredentialsFactory.cs deleted file mode 100644 index 682b1015f3c8..000000000000 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/IAWSCredentialsFactory.cs +++ /dev/null @@ -1,15 +0,0 @@ -using Amazon.Runtime; - -namespace AWSSDK.Extensions.NETCore.Setup -{ - /// - /// - /// - public interface IAWSCredentialsFactory - { - /// - /// Creates AWSCredentials - /// - AWSCredentials Create(); - } -} \ No newline at end of file diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index 5750ce9f7d8a..fae7be2a6564 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -153,94 +153,6 @@ public static IServiceCollection TryAddAWSCredentials( return collection; } - /// - /// - /// - /// - /// - /// - public static IServiceCollection AddAWSCredentialsFactory( - this IServiceCollection collection, - ServiceLifetime lifetime = ServiceLifetime.Singleton) - { - collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), CreateDefaultCredentialsFactory, lifetime)); - return collection; - } - - /// - /// - /// - /// - /// - /// - /// - public static IServiceCollection AddAWSCredentialsFactory( - this IServiceCollection collection, - Func implementationFactory, - ServiceLifetime lifetime = ServiceLifetime.Singleton) - { - collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), implementationFactory, lifetime)); - return collection; - } - - /// - /// - /// - /// - /// - /// - public static IServiceCollection AddAWSCredentialsFactory( - this IServiceCollection collection, - IAWSCredentialsFactory credentials) - { - collection.Add(new ServiceDescriptor(typeof(IAWSCredentialsFactory), credentials)); - return collection; - } - - /// - /// - /// - /// - /// - /// - public static IServiceCollection TryAddAWSCredentialsFactory( - this IServiceCollection collection, - ServiceLifetime lifetime = ServiceLifetime.Singleton) - { - collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), CreateDefaultCredentialsFactory, lifetime)); - return collection; - } - - /// - /// - /// - /// - /// - /// - /// - public static IServiceCollection TryAddAWSCredentialsFactory( - this IServiceCollection collection, - Func implementationFactory, - ServiceLifetime lifetime = ServiceLifetime.Singleton) - { - collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), implementationFactory, lifetime)); - return collection; - } - - /// - /// - /// - /// - /// - /// - public static IServiceCollection TryAddAWSCredentialsFactory( - this IServiceCollection collection, - IAWSCredentialsFactory credentials) - { - collection.TryAdd(new ServiceDescriptor(typeof(IAWSCredentialsFactory), credentials)); - return collection; - } - /// /// Adds the AWSOptions object to the dependency injection framework providing information /// that will be used to construct Amazon service clients if they haven't already been registered. @@ -478,23 +390,17 @@ private static object CreateServiceClient(AWSOptions options, IServiceProvide { var logger = sp.GetService(); var awsOptions = options ?? sp.GetService() ?? new AWSOptions(); - var credentialsFactory = sp.GetService() ?? new DefaultAWSCredentialsFactory(awsOptions, logger); + var credentialsFactory = sp.GetService() ?? sp.CreateDefaultAWSCredentials(options); var factory = new ClientFactory(awsOptions, credentialsFactory, logger); - return factory.CreateServiceClient(sp.GetService()); - } - - private static IAWSCredentialsFactory CreateDefaultCredentialsFactory(this IServiceProvider sp) - { - var options = sp.GetService() ?? new AWSOptions(); - return new DefaultAWSCredentialsFactory(options, sp.GetService()); + return factory.CreateServiceClient(); } - private static AWSCredentials CreateDefaultAWSCredentials(this IServiceProvider sp) + private static AWSCredentials CreateDefaultAWSCredentials(this IServiceProvider sp, AWSOptions options = null) { - var credentialsFactory = sp.GetService() ?? sp.CreateDefaultCredentialsFactory(); - return credentialsFactory.Create(); + options = options ?? sp.GetService() ?? new AWSOptions(); + return sp.GetService() ?? new DefaultAWSCredentials(options, sp.GetService()); } } } From 08520175d8e93b9fb271288bc5292d90c834a8bf Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 15:33:17 -0400 Subject: [PATCH 7/8] Fix infinite recursion with AddAWSService() --- .../ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index fae7be2a6564..cbb4a8e10552 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -400,7 +400,7 @@ private static object CreateServiceClient(AWSOptions options, IServiceProvide private static AWSCredentials CreateDefaultAWSCredentials(this IServiceProvider sp, AWSOptions options = null) { options = options ?? sp.GetService() ?? new AWSOptions(); - return sp.GetService() ?? new DefaultAWSCredentials(options, sp.GetService()); + return new DefaultAWSCredentials(options, sp.GetService()); } } } From af1865855aee3a9f2d9fdf7225007ff4672d9e76 Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 15:36:53 -0400 Subject: [PATCH 8/8] Fix another bug with AddAWSService() --- .../ServiceCollectionExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index cbb4a8e10552..35658fcc78ed 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -390,7 +390,7 @@ private static object CreateServiceClient(AWSOptions options, IServiceProvide { var logger = sp.GetService(); var awsOptions = options ?? sp.GetService() ?? new AWSOptions(); - var credentialsFactory = sp.GetService() ?? sp.CreateDefaultAWSCredentials(options); + var credentialsFactory = sp.GetService() ?? sp.CreateDefaultAWSCredentials(awsOptions); var factory = new ClientFactory(awsOptions, credentialsFactory, logger);