I want to inject database connection propertiy values, and others from AWS Secret Manager. (Something similary with this solution). So they can be read in an implementation of EnvironmentPostProcessor
and injected into spring like this:
props.put("spring.datasource.username", usernameValue);
props.put("spring.datasource.password", passwordValue);
environment.getPropertySources().addFirst(new PropertiesPropertySource("dbProps", props));
However, to accomplish this, I need an instance of AwsSecretManagerClient which can be obtained only after initializing properties. Which is the best solution to accomplish this? (and maybe a best practice)
Note: Application will be running in docker in AWS ECS, and the accessKey/secretKey will be different on each environment.
It looks like you have a potential chicken-egg problem here. You have a Spring Boot application that has application properties that you want to inject. You want these values to come from AWS Secrets Manager and you are using AWS ECS.
This will not work without a custom, non-standard solution.
In either case, you will need a properly configured Execution Task Role or Task Role in your ECS Task Definition to even allow you to get the value from AWS Secrets.
If you were really determined to inject it in as a property, you would have to do that BEFORE you invoke your SpringApplication in your main method. In this case, your Task Role would need Secrets access so you can use the AWS SDK to get them. This is a lot of pre-start code and is just a bit awkward when there are tools readily available to do this for you. I can imagine something like this
public static void main(String[] args) {
//write a bunch of code to instantiate the AwsSecretManagerClient
//and then pull out the values, to then put as properties.
//this code also has to account for when you are not running on AWS
//then start your Spring application
}
The suggested method here is to just use ECS to inject them for you.
For example, let's say you have spring.datasource.password
in your application properties (or yaml). The value is stored in a AWS Secret named DBPassword
and you want to use the Secrets value at runtime on ECS.
Your Task Definition should have a section like this
"secrets": [
{
"name": "SPRING_DATBASOURCE_PASSWORD",
"valueFrom": "arn:aws:secretsmanager:{{region}}:{{accountNumber}}:secret:DBPassword"
}
],
For this to happen, your Task Definition should have the Execution Role with permission:
{
"Action": [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
],
"Resource": "arn:aws:secretsmanager:{{region}}:{{accountNumber}}:secret:DBPassword",
"Effect": "Allow"
},
From there. You don't need to do anything else in your Spring application. Spring will override your application.yml/properties value based on the new environment variable that was set for you via ECS.
The beauty of this solution is that you don't need special code. Locally, you can develop with your application.yaml/properties and then in ECS, with a little infrastructure configuration, you get it auto-overwritten. No extra complex code to handle both local and AWS.