Hardcoding database credentials, API tokens, or encryption keys inside our application.properties or application.yml is a major security vulnerability. Even if our Git repository is private, committing plain-text secrets leaves our infrastructure exposed to credential leaks.
The industry-standard solution for cloud-native applications is to externalize sensitive configurations using a secure vault. In this step-by-step tutorial, we will learn exactly how to use Spring Cloud AWS to dynamically read and inject a multi-key JSON block from a single AWS Secrets Manager container right at application startup without needing a Spring Cloud Config Server.
Table of Contents
Why Use a Single JSON Secret Object?
AWS Secrets Manager charges a flat rate of $0.40 per secret per month. If we create an independent secret container for every individual property (database URL, password, API tokens), our cloud bill will add up fast.
By bundling our properties into a single, flat JSON Key/Value block, we only pay for one secret ($0.40/month total), while Spring Boot automatically expands the JSON elements into native environment properties.
Step 1: Create the JSON Secret in AWS
First, we need to store our production configuration keys inside AWS.
- Open your AWS Secrets Manager Console.
- Click Store a new secret and choose Other type of secret.
- Under the key/value options, click the Plaintext tab.
- Replace the sample text with your configuration keys formatted as a flat JSON object. Ensure you use the exact Spring Boot dot-notation keys:
Example
{
"spring.mongodb.uri": "mongodb+srv://db_admin:[email protected]/my_db",
"api.stripe.key": "sk_live_51Nx..."
}
- Click Next. Name your secret exactly
production/secrets. - Click Next through the remaining screens and click Store.
Step 2: Configure the Spring Cloud AWS BOM and Starter
The dependency below used was verified with Spring Boot 4.0.6 version.
To prevent dependency version mismatches that cause IllegalStateException or resolver failures, it is critical to use the official Spring Cloud AWS Bill of Materials (BOM). This synchronizes all underlying AWS Java SDK libraries natively.
For Maven (pom.xml)
We need to add the BOM inside our <dependencyManagement> section, then include the Secrets Manager starter inside our standard <dependencies> block:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-aws-dependencies</artifactId>
<version>4.0.2</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- AWS Secrets Manager Starter Managed by the BOM -->
<dependency>
<groupId>io.awspring.cloud</groupId>
<artifactId>spring-cloud-starter-aws-secrets-manager-config</artifactId>
</dependency>
</dependencies>
Step 3: Define the Config Import Rule
With the correct starter present, Spring Boot’s ConfigDataLocationResolver will instantly recognize the aws-secretsmanager: routing prefix.
Let’s clean out all sensitive credentials from our local src/main/resources/application.properties and import aws-secretmanager using the configuration property spring.config.import.
Example application.properties:
# Connect Spring Boot to AWS Secrets Manager Dynamically
spring.config.import=aws-secretsmanager:demo/secrets
# Explicitly designates which cloud data region contains the secret
spring.cloud.aws.region.static=us-east-1
# Enable AWS Secrets Manager
spring.cloud.aws.secretsmanager.enabled=true
Important
The secret name must match with the one we have created earlier step in AWS Secrets Manager and in the configuration properties.
So, the production/secrets we are defining in the application.properties is the one that we have created in AWS Secrets Manager.
And also we need to enable AWS Secrets Manager with the property spring.cloud.aws.secretsmanager.enable=true
Step 4: Access Properties Natively in Java
Because the JSON keys in our AWS container perfectly match Spring Boot’s built-in naming expectations, no custom Java parser code is required.
- Automatic Infrastructure Binding: The framework extracts
spring.mongodb.uriout of memory and automatically configures our MongoDB client connection driver pool instantly. - Custom Key Retrieval: For custom application configurations (like third-party API tokens), we can use standard
@Valueinjection anywhere in our codebase:
Example:
@RestController
public class SecretVerificationController {
@Value("${api.stripe.key}")
private String stripeApiKey;
// We can use stripeApiKey when we need.
}
Step 5: Secure our EC2 Deployment with IAM Instance Profiles
To safely run our application on an AWS EC2 instance without hardcoding AWS Access Keys (AWS_ACCESS_KEY_ID), we need to follow the least privilege security design pattern using an IAM Role by creating IAM policy and attach it to Instance Profile.
1. Create an IAM Least-Privilege Policy
Create a policy that allows your application to execute GetSecretValueonly on that exact secret ARN. (Note: The trailing -* wildcard is required because AWS appends a random unique suffix to secret ARNs).
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowSpringBootToReadProdSecrets",
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": "arn:aws:secretsmanager:us-east-1:123456789012:secret:production/secrets-*"
}
]
}
2. Attach the Profile to EC2
- Create an IAM Role targeting EC2 as the trusted service and attach the custom policy above.
- Head to the EC2 Dashboard, select target instance, and click Actions -> Security -> Modify IAM role.
- Attach this new role.
When the application starts on EC2, the Spring Cloud AWS infrastructure automatically connects to the internal EC2 Instance Metadata Service (IMDS), fetches temporary security tokens, and reads our configurations seamlessly.
Conclusion & Troubleshooting
We have successfully decoupled our system secrets from your codebase! Oour Git history is safe, and our production credentials are locked down inside an encrypted AWS vault.
Quick Troubleshooting Checklist:
- Getting a
StandardConfigDataLocationResolverfile extension error? Ensure your dependencies are refreshed and that you are using the modernio.awspring.cloudpackage, not the old legacyorg.springframework.cloudpackage. - Database not connecting? Ensure your AWS secret key is named exactly
spring.mongodb.uri. Legacy versions usedspring.data.mongodb.uri, which modern Spring Boot auto-configurations ignore.