AWS Aurora (both MySQL and Postgres) and MariaDB allow IAM authentication. With this authentication method, you don't need to use a password when you connect to a database. Instead, you use an authentication token. In this post, we are going to explore how to use IAM database authentication with EF Core and Aurora Postgres.
Before starting, you need to fulfill a few pre-requisites:
Enabling IAM Authentication in your Aurora Postgres database.
Have an IAM User with programmatic access.
Set up your AWS CLI locally.
The first step is to create an IAM policy using this template:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"rds-db:connect"
],
"Resource": [
"arn:aws:rds-db:[region]:[account-id]:dbuser:[db-cluster-resourceId]/[db-user-name]"
]
}
]
}
[region]
is the AWS Region for the database cluster. An example of a region isus-east-2
.[account-id]
is the AWS account number for the database cluster. An example of an account number is1234567890
.[db-cluster-resourceId]
is the identifier for the database cluster. An example of this identifier iscluster-ABCDEFGHIJKL01234
.[db-user-name]
is the name of the database account associated with IAM authentication.
Once we have that, we attach it to our IAM user. The next step is to create a database (we are using the postgres
user to run the following scripts):
CREATE DATABASE tasksmanager
WITH
ENCODING = 'UTF8';
And then a table inside this new database:
CREATE TABLE tasks(
id UUID PRIMARY KEY,
name VARCHAR(255)
);
And finally, the database user:
CREATE USER db_iam_user;
GRANT rds_iam TO db_iam_user;
GRANT postgres TO db_iam_user;
Now, we will create a .NET ASP Core Web API application. Add the following NuGet packages to that application:
In our appsetting.json
, we are going to add a ConnectionString
property:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionString": "host=[aurora-database-endpoint];Database=tasksmanager;Username=db_iam_user;SSL Mode=Require;TrustServerCertificate=true"
}
Add a file with the following content:
public class Task
{
public Guid Id { get; set; }
public string Name { get; set; }
private Task()
{
}
public Task(string name)
{
Id = Guid.NewGuid();
Name = name;
}
}
With that, we can create our DbContext
class:
public class ApplicationDbContext : DbContext
{
public DbSet<Task> Tasks { get; set; }
public ApplicationDbContext(DbContextOptions options) : base(options)
{
}
}
Now, add a ServiceCollectionExtensions.cs
file. Here is where all the magic happens:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddPostgreSQL(this IServiceCollection services, IConfiguration configuration)
{
services.AddDbContext<ApplicationDbContext>(options =>
{
var connectionString = configuration["ConnectionString"];
options.UseNpgsql(connectionString, npgsqlOption => { npgsqlOption.UseAwsIamAuthentication(); });
options.UseLowerCaseNamingConvention();
});
return services;
}
public static NpgsqlDbContextOptionsBuilder UseAwsIamAuthentication(this NpgsqlDbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.ProvidePasswordCallback(RequestAwsIamAuthToken);
return optionsBuilder;
}
static string RequestAwsIamAuthToken(string host, int port, string database, string username)
{
return RDSAuthTokenGenerator.GenerateAuthToken(host, port, username);
}
}
Then, add the following line to your Program.cs
file:
builder.Services.AddPostgreSQL(builder.Configuration);
Add a couple of models for our request and response:
public class RegisterTaskCommand
{
public string Name { get; set; }
}
public class ListTasksResult
{
public Guid Id { get; set; }
public string Name { get; set; }
}
And finally, our TaskController.cs
file:
[ApiController]
[Route("[controller]")]
public class TasksController : ControllerBase
{
private readonly ApplicationDbContext _context;
public TasksController(ApplicationDbContext context)
{
_context = context;
}
[HttpGet()]
public Task<ListTasksResult[]> ListTasks()
{
return _context.Tasks.Select(t => new ListTasksResult() { Id = t.Id, Name = t.Name }).ToArrayAsync();
}
[HttpPost()]
public async Task<Guid> RegisterTask(RegisterTaskCommand command)
{
var task = new Task(command.Name);
_context.Tasks.Add(task);
await _context.SaveChangesAsync();
return task.Id;
}
}
Run your application and test your passwordless connection against your database. Here, you can find all the code and database scripts.