Skip to content

Introduce [Try]AddAWSCredentials extension methods #3758

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 8 commits into
base: development
Choose a base branch
from
14 changes: 1 addition & 13 deletions extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -92,18 +92,6 @@ internal set
/// </summary>
public LoggingSetting Logging { get; set; }

/// <summary>
/// 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.
/// </summary>
/// <typeparam name="T">The service interface that a service client will be created for.</typeparam>
/// <returns>The service client that implements the service interface.</returns>
public T CreateServiceClient<T>() where T : class, IAmazonService
{
return new ClientFactory<T>(this).CreateServiceClient((ILogger)null, this) as T;
}

/// <summary>
/// Container for logging settings of the SDK
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using Amazon.Extensions.NETCore.Setup;
using Amazon.Runtime;

namespace AWSSDK.Extensions.NETCore.Setup
{
/// <summary>
///
/// </summary>
public static class AWSOptionsExtensions
{
/// <summary>
/// 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.
/// </summary>
/// <typeparam name="T">The service interface that a service client will be created for.</typeparam>
/// <returns>The service client that implements the service interface.</returns>
public static T CreateServiceClient<T>(this AWSOptions options)
where T : class, IAmazonService
{
var credentials = new DefaultAWSCredentials(options, null);
var clientFactory = new ClientFactory<T>(options, credentials, null);

return clientFactory.CreateServiceClient() as T;
}
}
}
106 changes: 19 additions & 87 deletions extensions/src/AWSSDK.Extensions.NETCore.Setup/ClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -36,68 +37,45 @@ internal class ClientFactory<T>
private static readonly Type[] EMPTY_TYPES = Array.Empty<Type>();
private static readonly object[] EMPTY_PARAMETERS = Array.Empty<object>();

private AWSOptions _awsOptions;
private readonly AWSOptions _options;
private readonly AWSCredentials _credentials;
private readonly ILogger _logger;

/// <summary>
/// Constructs an instance of the ClientFactory
/// </summary>
/// <param name="awsOptions">The AWS options used for creating service clients.</param>
internal ClientFactory(AWSOptions awsOptions)
{
_awsOptions = awsOptions;
}

/// <summary>
/// Creates the AWS service client that implements the service client interface. The AWSOptions object
/// will be searched for in the IServiceProvider.
/// </summary>
/// <param name="provider">The dependency injection provider.</param>
/// <returns>The AWS service client</returns>
internal object CreateServiceClient(IServiceProvider provider)
/// <param name="credentials"></param>
/// <param name="logger"></param>
internal ClientFactory(AWSOptions awsOptions, AWSCredentials credentials, ILogger logger)
{
var loggerFactory = provider.GetService<Microsoft.Extensions.Logging.ILoggerFactory>();
var logger = loggerFactory?.CreateLogger("AWSSDK");

var options = _awsOptions ?? provider.GetService<AWSOptions>();
if(options == null)
{
var configuration = provider.GetService<IConfiguration>();
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));
_credentials = credentials ?? throw new ArgumentNullException(nameof(credentials));
_logger = logger;
}

/// <summary>
/// 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.
/// </summary>
/// <param name="logger">Logger instance for writing diagnostic logs.</param>
/// <param name="options">The AWS options used for creating the service client.</param>
/// <returns>The AWS service client</returns>
internal IAmazonService CreateServiceClient(ILogger logger, AWSOptions options)
internal IAmazonService CreateServiceClient()
{
PerformGlobalConfig(logger, options);
var credentials = CreateCredentials(logger, options);
PerformGlobalConfig(_logger, _options);
var credentials = _credentials;

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;
}
Expand Down Expand Up @@ -165,52 +143,6 @@ private static AmazonServiceClient CreateClient(AWSCredentials credentials, Clie
#endif
}

/// <summary>
/// Creates the AWSCredentials using either the profile indicated from the AWSOptions object
/// of the SDK fallback credentials search.
/// </summary>
/// <param name="logger"></param>
/// <param name="options"></param>
/// <returns></returns>
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<AWSCredentials>();
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;
}

/// <summary>
/// Creates the ClientConfig object for the service client.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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
{
/// <summary>
///
/// </summary>
public class DefaultAWSCredentials : AWSCredentials
{
private readonly AWSOptions _options;
private readonly ILogger _logger;

/// <summary>
///
/// </summary>
/// <param name="awsOptions"></param>
/// <param name="logger"></param>
public DefaultAWSCredentials(AWSOptions awsOptions, ILogger logger)
{
_options = awsOptions;
_logger = logger;
}

/// <summary>
///
/// </summary>
/// <returns></returns>
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.GetCredentials();
}
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.GetCredentials();
}
else
{
_logger?.LogInformation($"Failed to find AWS credentials for the profile {_options.Profile}");
}
}
}

var credentials = DefaultIdentityResolverConfiguration.ResolveDefaultIdentity<AWSCredentials>();
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.GetCredentials();
}
}
}
Loading