CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-amazonaws--aws-java-sdk-secretsmanager

Java client library for AWS Secrets Manager enabling secure storage, management, and retrieval of secrets.

Pending
Overview
Eval results
Files

rotation.mddocs/

Rotation Management

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.

Rotation Concepts

  • AWSCURRENT: The current active version of the secret
  • AWSPENDING: The new version being created during rotation
  • Rotation Lambda: AWS Lambda function that performs the actual credential rotation
  • Rotation Rules: Configuration specifying when rotation should occur

Core Rotation Operations

Starting Rotation

RotateSecretRequest

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
}

RotateSecretResult

class RotateSecretResult {
    String arn;                               // Secret ARN
    String name;                              // Secret name
    String versionId;                         // Version ID of AWSPENDING version
}

RotationRulesType

class RotationRulesType {
    Long automaticallyAfterDays;              // Number of days between rotations (1-1000)
}

Usage Example

// 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);

Canceling Rotation

CancelRotateSecretRequest

class CancelRotateSecretRequest extends AmazonWebServiceRequest {
    String secretId;                          // Required: Secret identifier
}

CancelRotateSecretResult

class CancelRotateSecretResult {
    String arn;                               // Secret ARN
    String name;                              // Secret name
    String versionId;                         // Version ID of cancelled rotation
}

Usage Example

// 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());
}

Rotation Configuration

Configuring Rotation During Secret Creation

// 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);

Updating Rotation Configuration

// 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);

Version Stage Management

Understanding Version Stages

  • AWSCURRENT: The currently active secret version
  • AWSPENDING: A new version being created during rotation
  • Custom Stages: User-defined stages for specific use cases

Moving Version Stages

UpdateSecretVersionStageRequest

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
}

UpdateSecretVersionStageResult

class UpdateSecretVersionStageResult {
    String arn;                               // Secret ARN
    String name;                              // Secret name
}

Usage Example

// 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);

Rotation Monitoring

Checking Rotation Status

// 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);
    }
}

Handling Rotation States

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;
        }
    }
}

Advanced Rotation Scenarios

Database Credential Rotation

// 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");
}

Multi-User Rotation

// 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());
}

Custom Application Rotation

// 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());
    }
}

Error Handling

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());
}

Best Practices

  1. Lambda Function Setup: Ensure rotation Lambda has proper IAM permissions
  2. Testing: Test rotation in non-production environments first
  3. Monitoring: Set up CloudWatch alarms for rotation failures
  4. Rollback Plan: Know how to rollback using version stage management
  5. Network Access: Ensure Lambda can reach both Secrets Manager and target service
  6. Error Handling: Implement proper retry logic for transient failures
  7. Scheduling: Choose rotation intervals based on security requirements
  8. Multi-Region: Consider cross-region implications for replicated secrets

Install with Tessl CLI

npx tessl i tessl/maven-com-amazonaws--aws-java-sdk-secretsmanager

docs

index.md

policies-tags.md

replication.md

rotation.md

secret-management.md

utilities.md

tile.json