Java client library for AWS Secrets Manager enabling secure storage, management, and retrieval of secrets.
—
Automated secret rotation capabilities for seamless credential updates without service interruption. AWS Secrets Manager supports automatic rotation using AWS Lambda functions to update credentials in both the service and the secret.
class RotateSecretRequest extends AmazonWebServiceRequest {
String secretId; // Required: Secret identifier
String clientRequestToken; // Idempotency token
String rotationLambdaARN; // Lambda function ARN for rotation
RotationRulesType rotationRules; // Rotation schedule configuration
Boolean rotateImmediately; // Start rotation immediately
Boolean forceRotateSecretVersion; // Force rotation even if AWSPENDING exists
}class RotateSecretResult {
String arn; // Secret ARN
String name; // Secret name
String versionId; // Version ID of AWSPENDING version
}class RotationRulesType {
Long automaticallyAfterDays; // Number of days between rotations (1-1000)
}// Set up automatic rotation for database credentials
RotationRulesType rotationRules = new RotationRulesType()
.withAutomaticallyAfterDays(30L);
RotateSecretRequest rotateRequest = new RotateSecretRequest()
.withSecretId("rds/prod/masteruser")
.withRotationLambdaARN("arn:aws:lambda:us-west-2:123456789012:function:SecretsManagerRotation")
.withRotationRules(rotationRules)
.withRotateImmediately(true);
RotateSecretResult rotateResult = client.rotateSecret(rotateRequest);
System.out.println("Started rotation for secret: " + rotateResult.getName());
System.out.println("AWSPENDING version: " + rotateResult.getVersionId());
// Start immediate rotation without changing schedule
RotateSecretRequest immediateRotateRequest = new RotateSecretRequest()
.withSecretId("api/service/token")
.withRotateImmediately(true);
RotateSecretResult immediateResult = client.rotateSecret(immediateRotateRequest);class CancelRotateSecretRequest extends AmazonWebServiceRequest {
String secretId; // Required: Secret identifier
}class CancelRotateSecretResult {
String arn; // Secret ARN
String name; // Secret name
String versionId; // Version ID of cancelled rotation
}// Cancel an in-progress rotation
CancelRotateSecretRequest cancelRequest = new CancelRotateSecretRequest()
.withSecretId("rds/prod/masteruser");
try {
CancelRotateSecretResult cancelResult = client.cancelRotateSecret(cancelRequest);
System.out.println("Cancelled rotation for: " + cancelResult.getName());
} catch (InvalidRequestException e) {
System.err.println("No rotation in progress or cannot cancel: " + e.getMessage());
}// Create secret with rotation configuration
RotationRulesType rotationRules = new RotationRulesType()
.withAutomaticallyAfterDays(45L);
CreateSecretRequest createWithRotation = new CreateSecretRequest()
.withName("database/credentials")
.withSecretString("{\"username\":\"admin\",\"password\":\"initialPassword\"}")
.withDescription("Database credentials with automatic rotation");
// First create the secret
CreateSecretResult createResult = client.createSecret(createWithRotation);
// Then configure rotation
RotateSecretRequest configureRotation = new RotateSecretRequest()
.withSecretId(createResult.getName())
.withRotationLambdaARN("arn:aws:lambda:us-west-2:123456789012:function:DatabaseRotation")
.withRotationRules(rotationRules);
RotateSecretResult rotationConfig = client.rotateSecret(configureRotation);// Update rotation schedule
RotationRulesType newRules = new RotationRulesType()
.withAutomaticallyAfterDays(60L); // Change from 30 to 60 days
RotateSecretRequest updateRotation = new RotateSecretRequest()
.withSecretId("rds/prod/masteruser")
.withRotationRules(newRules);
RotateSecretResult updateResult = client.rotateSecret(updateRotation);
// Change rotation Lambda function
RotateSecretRequest updateLambda = new RotateSecretRequest()
.withSecretId("api/credentials")
.withRotationLambdaARN("arn:aws:lambda:us-west-2:123456789012:function:NewRotationFunction");
RotateSecretResult lambdaUpdate = client.rotateSecret(updateLambda);class UpdateSecretVersionStageRequest extends AmazonWebServiceRequest {
String secretId; // Required: Secret identifier
String versionStage; // Required: Stage to move
String clientRequestToken; // Idempotency token
String moveToVersionId; // Version to move stage to
String removeFromVersionId; // Version to remove stage from
}class UpdateSecretVersionStageResult {
String arn; // Secret ARN
String name; // Secret name
}// Promote AWSPENDING to AWSCURRENT (complete rotation)
UpdateSecretVersionStageRequest promoteRequest = new UpdateSecretVersionStageRequest()
.withSecretId("rds/prod/masteruser")
.withVersionStage("AWSCURRENT")
.withMoveToVersionId("new-version-id")
.withRemoveFromVersionId("old-version-id");
UpdateSecretVersionStageResult promoteResult = client.updateSecretVersionStage(promoteRequest);
// Create custom stage for testing
UpdateSecretVersionStageRequest testStageRequest = new UpdateSecretVersionStageRequest()
.withSecretId("api/credentials")
.withVersionStage("TESTING")
.withMoveToVersionId("test-version-id");
UpdateSecretVersionStageResult testResult = client.updateSecretVersionStage(testStageRequest);
// Rollback by moving AWSCURRENT to previous version
UpdateSecretVersionStageRequest rollbackRequest = new UpdateSecretVersionStageRequest()
.withSecretId("database/password")
.withVersionStage("AWSCURRENT")
.withMoveToVersionId("previous-version-id")
.withRemoveFromVersionId("current-version-id");
UpdateSecretVersionStageResult rollbackResult = client.updateSecretVersionStage(rollbackRequest);// Check rotation status using describe
DescribeSecretRequest statusRequest = new DescribeSecretRequest()
.withSecretId("rds/prod/masteruser");
DescribeSecretResult statusResult = client.describeSecret(statusRequest);
System.out.println("Rotation Enabled: " + statusResult.getRotationEnabled());
System.out.println("Rotation Lambda: " + statusResult.getRotationLambdaARN());
System.out.println("Last Rotated: " + statusResult.getLastRotatedDate());
System.out.println("Next Rotation: " + statusResult.getNextRotationDate());
if (statusResult.getRotationRules() != null) {
Long days = statusResult.getRotationRules().getAutomaticallyAfterDays();
System.out.println("Rotation Interval: " + days + " days");
}
// Check version stages to see rotation progress
Map<String, List<String>> versionStages = statusResult.getVersionIdsToStages();
for (Map.Entry<String, List<String>> entry : versionStages.entrySet()) {
String versionId = entry.getKey();
List<String> stages = entry.getValue();
if (stages.contains("AWSPENDING")) {
System.out.println("Rotation in progress - AWSPENDING version: " + versionId);
}
if (stages.contains("AWSCURRENT")) {
System.out.println("Current active version: " + versionId);
}
}public class RotationStatusChecker {
public enum RotationState {
NOT_CONFIGURED,
CONFIGURED_NOT_ROTATING,
ROTATION_IN_PROGRESS,
ROTATION_FAILED
}
public RotationState checkRotationState(AWSSecretsManager client, String secretId) {
try {
DescribeSecretResult result = client.describeSecret(
new DescribeSecretRequest().withSecretId(secretId));
// Check if rotation is configured
if (!Boolean.TRUE.equals(result.getRotationEnabled())) {
return RotationState.NOT_CONFIGURED;
}
// Check for AWSPENDING version
Map<String, List<String>> versions = result.getVersionIdsToStages();
boolean hasPending = versions.values().stream()
.anyMatch(stages -> stages.contains("AWSPENDING"));
if (hasPending) {
// Check if rotation is stuck (older than expected)
Date lastRotated = result.getLastRotatedDate();
long hoursSinceRotation = (System.currentTimeMillis() -
lastRotated.getTime()) / (1000 * 60 * 60);
if (hoursSinceRotation > 24) { // Rotation taking too long
return RotationState.ROTATION_FAILED;
}
return RotationState.ROTATION_IN_PROGRESS;
}
return RotationState.CONFIGURED_NOT_ROTATING;
} catch (Exception e) {
System.err.println("Error checking rotation state: " + e.getMessage());
return RotationState.ROTATION_FAILED;
}
}
}// Rotate RDS database master password
public void rotateRDSMasterPassword(String secretId, String dbInstanceId) {
// The rotation Lambda function will:
// 1. Create new password in AWSPENDING version
// 2. Update RDS master password
// 3. Test new credentials
// 4. Move AWSCURRENT stage to new version
RotateSecretRequest rotateRequest = new RotateSecretRequest()
.withSecretId(secretId)
.withRotationLambdaARN("arn:aws:lambda:region:account:function:SecretsManagerRDSMySQLRotationSingleUser")
.withRotateImmediately(true);
try {
RotateSecretResult result = client.rotateSecret(rotateRequest);
System.out.println("Started RDS rotation: " + result.getVersionId());
// Wait for rotation to complete
waitForRotationCompletion(secretId);
} catch (Exception e) {
System.err.println("Rotation failed: " + e.getMessage());
}
}
private void waitForRotationCompletion(String secretId) {
int maxWaitMinutes = 15;
int waitIntervalSeconds = 30;
int attempts = (maxWaitMinutes * 60) / waitIntervalSeconds;
for (int i = 0; i < attempts; i++) {
try {
DescribeSecretResult result = client.describeSecret(
new DescribeSecretRequest().withSecretId(secretId));
Map<String, List<String>> versions = result.getVersionIdsToStages();
boolean hasPending = versions.values().stream()
.anyMatch(stages -> stages.contains("AWSPENDING"));
if (!hasPending) {
System.out.println("Rotation completed successfully");
return;
}
Thread.sleep(waitIntervalSeconds * 1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
} catch (Exception e) {
System.err.println("Error checking rotation status: " + e.getMessage());
}
}
System.err.println("Rotation did not complete within expected time");
}// Rotate credentials for multi-user scenarios (alternating users)
public void rotateMultiUserCredentials(String secretId) {
RotateSecretRequest rotateRequest = new RotateSecretRequest()
.withSecretId(secretId)
.withRotationLambdaARN("arn:aws:lambda:region:account:function:SecretsManagerRDSMySQLRotationMultiUser")
.withRotationRules(new RotationRulesType().withAutomaticallyAfterDays(30L))
.withRotateImmediately(true);
RotateSecretResult result = client.rotateSecret(rotateRequest);
System.out.println("Started multi-user rotation: " + result.getVersionId());
}// Rotate API keys or application-specific credentials
public void rotateAPIKey(String secretId, String apiEndpoint) {
RotateSecretRequest rotateRequest = new RotateSecretRequest()
.withSecretId(secretId)
.withRotationLambdaARN("arn:aws:lambda:region:account:function:CustomAPIKeyRotation")
.withRotateImmediately(true);
try {
RotateSecretResult result = client.rotateSecret(rotateRequest);
System.out.println("Started API key rotation: " + result.getVersionId());
// The Lambda function should:
// 1. Generate new API key via the service API
// 2. Store new key in AWSPENDING version
// 3. Test new key works
// 4. Optionally revoke old key
// 5. Move AWSCURRENT stage to new version
} catch (Exception e) {
System.err.println("API key rotation failed: " + e.getMessage());
}
}Common rotation-related exceptions:
try {
RotateSecretResult result = client.rotateSecret(rotateRequest);
} catch (ResourceNotFoundException e) {
System.err.println("Secret not found: " + e.getMessage());
} catch (InvalidParameterException e) {
System.err.println("Invalid rotation configuration: " + e.getMessage());
} catch (InvalidRequestException e) {
System.err.println("Rotation cannot be performed: " + e.getMessage());
} catch (LimitExceededException e) {
System.err.println("Too many rotation requests: " + e.getMessage());
} catch (PreconditionNotMetException e) {
System.err.println("Rotation precondition failed: " + e.getMessage());
} catch (AWSSecretsManagerException e) {
System.err.println("Rotation service error: " + e.getMessage());
}
// Handle rotation cancellation errors
try {
CancelRotateSecretResult result = client.cancelRotateSecret(cancelRequest);
} catch (InvalidRequestException e) {
// Common reasons:
// - No rotation in progress
// - Rotation too far along to cancel
// - Lambda function already executing
System.err.println("Cannot cancel rotation: " + e.getMessage());
}Install with Tessl CLI
npx tessl i tessl/maven-com-amazonaws--aws-java-sdk-secretsmanager