I am trying to assume another role from my .NET Web API application running inside ECS Fargate. I am using the AWS SDK for .NET. The role that the container uses has been given permissions to assume another role in another AWS account. It works for some calls, but not for all.
I have tried to assume the other account role in Program.cs, which works:
Program.cs:
// the options are passed as env variables, which is where GetAWSOptions() gets them from in this case
var awsOptions = builder.Configuration.GetAWSOptions();
using (var stsClient = new AmazonSecurityTokenServiceClient(RegionEndpoint.EUWest1))
{
// Assume the tenanted role
var assumeRoleResponse = await stsClient.AssumeRoleAsync(new AssumeRoleRequest
{
RoleArn = roleArn,
RoleSessionName = $"api-{DateTime.UtcNow:yyyyMMddHHmmss}"
}).ConfigureAwait(false);
awsOptions.Credentials = new SessionAWSCredentials(
assumeRoleResponse.Credentials.AccessKeyId,
assumeRoleResponse.Credentials.SecretAccessKey,
assumeRoleResponse.Credentials.SessionToken);
}
builder.Services.AddDefaultAWSOptions(awsOptions);
builder.Services.AddAWSService<IAmazonDynamoDB>();
NinjectBindings.cs:
public static IKernel CreateBindings(this IKernel kernel, IApplicationBuilder app,
IWebHostEnvironment webHostEnvironment,
Func<IContext, Scope> RequestScope)
{
var dynamoDb = app.ApplicationServices.GetRequiredService<IAmazonDynamoDB>();
kernel.Bind<AWSOptions>().ToMethod(ctx => app.ApplicationServices.GetRequiredService<AWSOptions>());
kernel.Bind<IAmazonDynamoDB>().ToMethod((ctx => app.ApplicationServices.GetRequiredService<IAmazonDynamoDB>()));
}
//... other stuff here, including a service that instantiates an instance of DynamoDB
The issue I have is that in NinjectBindings.cs, when the service that calls DynamoDB is instantiated, it uses the ECS task-related role instead of the one assumed and configured in Program.cs.
What am I doing wrong?
If your service depends on credentials for an assumed role, it's better to make this dependency explicit.
Instead of using a parameterless constructor for creating an instance of
IAmazonDynamoDB, put a factory to your DI registration method which would assume the role you need first, and then pass this role's credentials to the constructor ofAmazonDynamoDBexplicitly.You can event make this factory async, if you register a delegate returning
Task<IAmazonDynamoDb>instead of the interface itself. If your register your dynamo client as a singleton, it will effectively remain a singleton (it will be instantiated once, cached in the task and reused after this task will be awaited by the consumers).I'm not familiar with Ninject, but you seem to rely on the fact that changes that you make to an object returned by
Configuration.GetAWSOptions()will propagate to the clients.This might or might not work (depending on whether this object is a singleton implicitly used by the clients), but even if it works, it's not the best practice.