Kralizek / AWSSecretsManagerConfigurationExtensions

This repository contains a provider for Microsoft.Extensions.Configuration that retrieves secrets stored in AWS Secrets Manager.
MIT License
219 stars 43 forks source link

Flatten database secrets to ConnectionString entry? #67

Closed rossobianero closed 1 year ago

rossobianero commented 2 years ago

I am sorry if this has been covered already. I cannot find anything like it in discussion.

AWS database secrets are dictionaries. Say I have specified a DB secret for an application called MyApplication: {"username":"myuser","password":"mypassword","engine":"sqlserver","host":"mysqlserver","port":"1433","dbname":"mysqldb"}

I would like to convert this to a ConnectionString entry in my configuration, like this: "ConnectionStrings" { "MyApplicationConnectionString": "Data Source=mysqlserver;Database=mysqlserver;Integrated Security=false;User ID=myuser;Password=mypassword;" }

But the configuration builder treats each item in the dictionary as a single configuration entry. So there is one entry for "username," another for "password," and so on. And no way I can find to flatten them into a single entry.

I considered using KeyGenerator but that only will allow mapping from each individual entry in the secret (e.g. "username," "password," etc.) to new entries in the destination config space, one by one.

avalara-stephen-hickey commented 1 year ago

IMO your options are to change the secret to be the entire connection string as a single item or to manually build the connection string from configuration like this:

var user = Configuration["MyApplication:UserName"];
var password = Configuration["MyApplication:Password"];
var connectionString = new StringBuilder("Data Source=");
connectionString.Append(...);
// You get the point.
ConnerOrth commented 1 year ago

You are free to use below code snippet to have a reusable way to flatten the 'amazon rds as a amazon secret' with implicit conversion to string. The only thing you need to provide in the appsettings.json connectionstrings section is the database/initial catalog name.

var configuration = host.Services.GetService<IConfiguration>();
AmazonRDSConnectionString connectionstringAsObject = GetConnectionStringFromAWSSecret(configuration);
string connectionstring = connectionstringAsObject;//implicit conversion to string.

var json = JsonSerializer.Serialize(connectionstringAsObject, new JsonSerializerOptions() { WriteIndented = true });

Console.WriteLine(json);
await host.RunAsync();

static AmazonRDSConnectionString GetConnectionStringFromAWSSecret(IConfiguration configuration)
{
    var section = configuration.GetSection("ConnectionStrings");
    AmazonRDSConnectionString amazonRDS = new();
    section.Bind(amazonRDS);
    return amazonRDS;
}

public sealed class AmazonRDSConnectionString
{
    //Amazon Secret Manager will provide this value
    public string Username { get; set; }

    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    //Amazon Secret Manager will provide this value
    public string Password { get;  set; }
    //Amazon Secret Manager will provide this value
    public string Engine { get; set; }
    //Amazon Secret Manager will provide this value
    public string Host { get; set; }
    //Amazon Secret Manager will provide this value
    public string Port { get; set; }
    //Amazon Secret Manager will provide this value
    public string DBInstanceIdentifier { get; set; }

    //You provide this inside the appsettings.json
    // "ConnectionStrings":{
    //      "Database": "NameOfDatabase/Catalog"
    // }
    public string Database { get; set; }

    //store the result so we only ever have to run the stringbuilder once
    private string? connectionString = null;
    public override string ToString()
    {
        if (connectionString != null) return connectionString;

        StringBuilder sb = new StringBuilder();
        if (!string.IsNullOrWhiteSpace(Database))
        {
            sb.Append("Database=")
                .Append(Database)
                .Append(';');
        }
        if (!string.IsNullOrWhiteSpace(Host))
        {
            sb.Append("Server=")
                .Append(Host);
            if (!string.IsNullOrWhiteSpace(Port))
            {
                sb.Append(',')
                    .Append(Port);
            }
            sb.Append(';');
        }
        if (!string.IsNullOrWhiteSpace(Username))
        {
            sb.Append("User ID=")
                .Append(Username)
                .Append(';');
        }
        if (!string.IsNullOrWhiteSpace(Password))
        {
            sb.Append("Password=")
                .Append(Password)
                .Append(';');
        }
        return connectionString = sb.ToString();
    }

    public static implicit operator string(AmazonRDSConnectionString connectionSettings)
    {
        return connectionSettings.ToString();
    }
}