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

replication.mddocs/

Replication Management

Multi-region secret replication for high availability, disaster recovery, and global application deployment. AWS Secrets Manager supports cross-region replication to ensure secrets are available where your applications need them.

Replication Concepts

  • Primary Region: The original region where the secret was created
  • Replica Regions: Additional regions where the secret is replicated
  • Replication Status: Current state of replication per region
  • KMS Key Management: Each region can use different KMS keys for encryption

Core Replication Operations

Setting Up Replication

ReplicateSecretToRegionsRequest

class ReplicateSecretToRegionsRequest extends AmazonWebServiceRequest {
    String secretId;                          // Required: Secret identifier
    List<ReplicaRegionType> addReplicaRegions; // Required: Regions to replicate to
    Boolean forceOverwriteReplicaSecret;      // Overwrite existing replica secrets
}

ReplicateSecretToRegionsResult

class ReplicateSecretToRegionsResult {
    String arn;                               // Primary secret ARN
    List<ReplicationStatusType> replicationStatus; // Replication status per region
}

ReplicaRegionType

class ReplicaRegionType {
    String region;                            // Required: AWS region code
    String kmsKeyId;                          // KMS key ID for encryption in this region
}

ReplicationStatusType

class ReplicationStatusType {
    String region;                            // AWS region
    String kmsKeyId;                          // KMS key ID used
    String status;                            // Replication status (InSync, Failed, InProgress)
    String statusMessage;                     // Detailed status message
    Date lastAccessedDate;                    // Last access date in this region
}

Usage Example

// Replicate secret to multiple regions
List<ReplicaRegionType> replicaRegions = new ArrayList<>();

// Replicate to us-east-1 with default KMS key
replicaRegions.add(new ReplicaRegionType()
    .withRegion("us-east-1"));

// Replicate to eu-west-1 with specific KMS key
replicaRegions.add(new ReplicaRegionType()
    .withRegion("eu-west-1")
    .withKmsKeyId("arn:aws:kms:eu-west-1:123456789012:key/12345678-1234-1234-1234-123456789012"));

ReplicateSecretToRegionsRequest replicateRequest = new ReplicateSecretToRegionsRequest()
    .withSecretId("prod/database/credentials")
    .withAddReplicaRegions(replicaRegions)
    .withForceOverwriteReplicaSecret(false);

ReplicateSecretToRegionsResult replicateResult = client.replicateSecretToRegions(replicateRequest);

// Check replication status
for (ReplicationStatusType status : replicateResult.getReplicationStatus()) {
    System.out.println("Region: " + status.getRegion() + 
                      " Status: " + status.getStatus());
}

Creating Secrets with Replication

// Create secret with immediate replication
List<ReplicaRegionType> initialReplicas = new ArrayList<>();
initialReplicas.add(new ReplicaRegionType().withRegion("us-east-1"));
initialReplicas.add(new ReplicaRegionType().withRegion("ap-southeast-1"));

CreateSecretRequest createWithReplication = new CreateSecretRequest()
    .withName("global/api/key")
    .withSecretString("api-key-value")
    .withDescription("Global API key with multi-region replication")
    .withAddReplicaRegions(initialReplicas);

CreateSecretResult createResult = client.createSecret(createWithReplication);

// Check initial replication status
for (ReplicationStatusType status : createResult.getReplicationStatus()) {
    System.out.println("Initial replication to " + status.getRegion() + 
                      ": " + status.getStatus());
}

Managing Existing Replication

Adding New Replica Regions

// Add additional regions to existing replication
List<ReplicaRegionType> newRegions = new ArrayList<>();
newRegions.add(new ReplicaRegionType()
    .withRegion("ap-northeast-1")
    .withKmsKeyId("arn:aws:kms:ap-northeast-1:123456789012:key/asia-key-id"));

ReplicateSecretToRegionsRequest addRegionsRequest = new ReplicateSecretToRegionsRequest()
    .withSecretId("global/api/key")
    .withAddReplicaRegions(newRegions);

ReplicateSecretToRegionsResult addResult = client.replicateSecretToRegions(addRegionsRequest);
System.out.println("Added replication to new regions");

Removing Replica Regions

RemoveRegionsFromReplicationRequest

class RemoveRegionsFromReplicationRequest extends AmazonWebServiceRequest {
    String secretId;                          // Required: Secret identifier
    List<String> removeReplicaRegions;        // Required: Region codes to remove
}

RemoveRegionsFromReplicationResult

class RemoveRegionsFromReplicationResult {
    String arn;                               // Primary secret ARN
    List<ReplicationStatusType> replicationStatus; // Updated replication status
}

Usage Example

// Remove replication from specific regions
List<String> regionsToRemove = Arrays.asList("us-east-1", "ap-southeast-1");

RemoveRegionsFromReplicationRequest removeRequest = new RemoveRegionsFromReplicationRequest()
    .withSecretId("global/api/key")
    .withRemoveReplicaRegions(regionsToRemove);

RemoveRegionsFromReplicationResult removeResult = client.removeRegionsFromReplication(removeRequest);

// Check remaining replications
for (ReplicationStatusType status : removeResult.getReplicationStatus()) {
    System.out.println("Remaining replica in: " + status.getRegion());
}

Stopping Replication to Replica

StopReplicationToReplicaRequest

class StopReplicationToReplicaRequest extends AmazonWebServiceRequest {
    String secretId;                          // Required: Secret identifier (from replica region)
}

StopReplicationToReplicaResult

class StopReplicationToReplicaResult {
    String arn;                               // Replica secret ARN
}

Usage Example

// Stop replication when called from the replica region
// This promotes the replica to a standalone secret
StopReplicationToReplicaRequest stopRequest = new StopReplicationToReplicaRequest()
    .withSecretId("global/api/key");

// This call must be made from the replica region you want to stop
StopReplicationToReplicaResult stopResult = replicaRegionClient.stopReplicationToReplica(stopRequest);
System.out.println("Stopped replication for: " + stopResult.getArn());

Replication Monitoring and Status

Checking Replication Status

// Get detailed replication status
DescribeSecretRequest statusRequest = new DescribeSecretRequest()
    .withSecretId("global/api/key");

DescribeSecretResult statusResult = client.describeSecret(statusRequest);

System.out.println("Primary Region: " + statusResult.getPrimaryRegion());

if (statusResult.getReplicationStatus() != null) {
    for (ReplicationStatusType status : statusResult.getReplicationStatus()) {
        System.out.println("Region: " + status.getRegion());
        System.out.println("  Status: " + status.getStatus());
        System.out.println("  KMS Key: " + status.getKmsKeyId());
        System.out.println("  Message: " + status.getStatusMessage());
        System.out.println("  Last Accessed: " + status.getLastAccessedDate());
    }
} else {
    System.out.println("No replication configured");
}

Replication Status Values

enum StatusType {
    InSync,                                   // Replication is current and healthy
    Failed,                                   // Replication failed
    InProgress                                // Replication is in progress
}

Monitoring Replication Health

public class ReplicationMonitor {
    
    public void checkReplicationHealth(AWSSecretsManager client, String secretId) {
        try {
            DescribeSecretResult result = client.describeSecret(
                new DescribeSecretRequest().withSecretId(secretId));
            
            List<ReplicationStatusType> statuses = result.getReplicationStatus();
            if (statuses == null || statuses.isEmpty()) {
                System.out.println("No replication configured");
                return;
            }
            
            boolean allHealthy = true;
            for (ReplicationStatusType status : statuses) {
                String region = status.getRegion();
                String statusValue = status.getStatus();
                
                switch (statusValue) {
                    case "InSync":
                        System.out.println("✓ " + region + ": Healthy");
                        break;
                    case "InProgress":
                        System.out.println("⏳ " + region + ": Syncing...");
                        allHealthy = false;
                        break;
                    case "Failed":
                        System.err.println("✗ " + region + ": FAILED - " + 
                                         status.getStatusMessage());
                        allHealthy = false;
                        break;
                    default:
                        System.out.println("? " + region + ": Unknown status - " + statusValue);
                        allHealthy = false;
                }
            }
            
            if (allHealthy) {
                System.out.println("All replications are healthy");
            } else {
                System.out.println("Some replications need attention");
            }
            
        } catch (Exception e) {
            System.err.println("Error checking replication health: " + e.getMessage());
        }
    }
}

Advanced Replication Scenarios

Cross-Region Disaster Recovery

// Set up disaster recovery replication
public void setupDisasterRecovery(String primarySecretId, String drRegion) {
    List<ReplicaRegionType> drReplicas = new ArrayList<>();
    
    // Use region-specific KMS key for DR region
    drReplicas.add(new ReplicaRegionType()
        .withRegion(drRegion)
        .withKmsKeyId("arn:aws:kms:" + drRegion + ":123456789012:key/dr-key-id"));
    
    ReplicateSecretToRegionsRequest drRequest = new ReplicateSecretToRegionsRequest()
        .withSecretId(primarySecretId)
        .withAddReplicaRegions(drReplicas);
    
    try {
        ReplicateSecretToRegionsResult result = client.replicateSecretToRegions(drRequest);
        System.out.println("DR replication configured for region: " + drRegion);
        
        // Wait for initial sync
        waitForReplicationSync(primarySecretId, drRegion);
        
    } catch (Exception e) {
        System.err.println("DR setup failed: " + e.getMessage());
    }
}

private void waitForReplicationSync(String secretId, String targetRegion) {
    int maxWaitMinutes = 10;
    int waitIntervalSeconds = 15;
    
    for (int i = 0; i < (maxWaitMinutes * 60 / waitIntervalSeconds); i++) {
        try {
            DescribeSecretResult result = client.describeSecret(
                new DescribeSecretRequest().withSecretId(secretId));
            
            List<ReplicationStatusType> statuses = result.getReplicationStatus();
            for (ReplicationStatusType status : statuses) {
                if (targetRegion.equals(status.getRegion())) {
                    if ("InSync".equals(status.getStatus())) {
                        System.out.println("Replication sync completed for " + targetRegion);
                        return;
                    } else if ("Failed".equals(status.getStatus())) {
                        System.err.println("Replication failed: " + status.getStatusMessage());
                        return;
                    }
                }
            }
            
            Thread.sleep(waitIntervalSeconds * 1000);
            
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            break;
        } catch (Exception e) {
            System.err.println("Error waiting for sync: " + e.getMessage());
        }
    }
    
    System.err.println("Replication sync did not complete within expected time");
}

Global Application Deployment

// Configure replication for global application
public void configureGlobalReplication(String secretId) {
    List<ReplicaRegionType> globalRegions = new ArrayList<>();
    
    // North America
    globalRegions.add(new ReplicaRegionType().withRegion("us-east-1"));
    globalRegions.add(new ReplicaRegionType().withRegion("us-west-2"));
    
    // Europe
    globalRegions.add(new ReplicaRegionType().withRegion("eu-west-1"));
    globalRegions.add(new ReplicaRegionType().withRegion("eu-central-1"));
    
    // Asia Pacific
    globalRegions.add(new ReplicaRegionType().withRegion("ap-southeast-1"));
    globalRegions.add(new ReplicaRegionType().withRegion("ap-northeast-1"));
    
    ReplicateSecretToRegionsRequest globalRequest = new ReplicateSecretToRegionsRequest()
        .withSecretId(secretId)
        .withAddReplicaRegions(globalRegions);
    
    try {
        ReplicateSecretToRegionsResult result = client.replicateSecretToRegions(globalRequest);
        System.out.println("Global replication configured");
        
        // Monitor all regions
        monitorGlobalReplication(secretId);
        
    } catch (Exception e) {
        System.err.println("Global replication setup failed: " + e.getMessage());
    }
}

private void monitorGlobalReplication(String secretId) {
    DescribeSecretResult result = client.describeSecret(
        new DescribeSecretRequest().withSecretId(secretId));
    
    Map<String, Integer> statusCounts = new HashMap<>();
    
    for (ReplicationStatusType status : result.getReplicationStatus()) {
        String statusValue = status.getStatus();
        statusCounts.put(statusValue, statusCounts.getOrDefault(statusValue, 0) + 1);
    }
    
    System.out.println("Global Replication Status Summary:");
    for (Map.Entry<String, Integer> entry : statusCounts.entrySet()) {
        System.out.println("  " + entry.getKey() + ": " + entry.getValue() + " regions");
    }
}

Updating KMS Keys for Replicas

// Update KMS key for specific replica regions
public void updateReplicaKMSKeys(String secretId, Map<String, String> regionKeyMap) {
    // Remove existing replicas
    List<String> regionsToUpdate = new ArrayList<>(regionKeyMap.keySet());
    
    RemoveRegionsFromReplicationRequest removeRequest = new RemoveRegionsFromReplicationRequest()
        .withSecretId(secretId)
        .withRemoveReplicaRegions(regionsToUpdate);
    
    try {
        client.removeRegionsFromReplication(removeRequest);
        
        // Wait a moment for cleanup
        Thread.sleep(5000);
        
        // Re-add with new KMS keys
        List<ReplicaRegionType> updatedReplicas = new ArrayList<>();
        for (Map.Entry<String, String> entry : regionKeyMap.entrySet()) {
            updatedReplicas.add(new ReplicaRegionType()
                .withRegion(entry.getKey())
                .withKmsKeyId(entry.getValue()));
        }
        
        ReplicateSecretToRegionsRequest addRequest = new ReplicateSecretToRegionsRequest()
            .withSecretId(secretId)
            .withAddReplicaRegions(updatedReplicas);
        
        ReplicateSecretToRegionsResult result = client.replicateSecretToRegions(addRequest);
        System.out.println("Updated KMS keys for replica regions");
        
    } catch (Exception e) {
        System.err.println("KMS key update failed: " + e.getMessage());
    }
}

Cross-Region Client Management

Multi-Region Secret Access

// Access secrets from different regions
public class CrossRegionSecretAccess {
    
    private Map<String, AWSSecretsManager> regionClients = new HashMap<>();
    
    public CrossRegionSecretAccess(List<String> regions) {
        for (String region : regions) {
            regionClients.put(region, AWSSecretsManagerClientBuilder.standard()
                .withRegion(region)
                .build());
        }
    }
    
    public GetSecretValueResult getSecretFromClosestRegion(String secretName, 
                                                          List<String> preferredRegions) {
        for (String region : preferredRegions) {
            AWSSecretsManager client = regionClients.get(region);
            if (client != null) {
                try {
                    GetSecretValueRequest request = new GetSecretValueRequest()
                        .withSecretId(secretName);
                    
                    return client.getSecretValue(request);
                    
                } catch (ResourceNotFoundException e) {
                    // Try next region
                    continue;
                } catch (Exception e) {
                    System.err.println("Error accessing secret in " + region + ": " + e.getMessage());
                    continue;
                }
            }
        }
        
        throw new RuntimeException("Secret not accessible from any preferred region");
    }
    
    public void shutdown() {
        for (AWSSecretsManager client : regionClients.values()) {
            client.shutdown();
        }
    }
}

Failover Strategy

public class SecretFailoverManager {
    
    public String getSecretWithFailover(String secretName, String primaryRegion, 
                                       List<String> backupRegions) {
        
        // Try primary region first
        try {
            AWSSecretsManager primaryClient = AWSSecretsManagerClientBuilder.standard()
                .withRegion(primaryRegion)
                .build();
                
            GetSecretValueResult result = primaryClient.getSecretValue(
                new GetSecretValueRequest().withSecretId(secretName));
            
            primaryClient.shutdown();
            return result.getSecretString();
            
        } catch (Exception e) {
            System.err.println("Primary region failed: " + e.getMessage());
        }
        
        // Try backup regions
        for (String backupRegion : backupRegions) {
            try {
                AWSSecretsManager backupClient = AWSSecretsManagerClientBuilder.standard()
                    .withRegion(backupRegion)
                    .build();
                    
                GetSecretValueResult result = backupClient.getSecretValue(
                    new GetSecretValueRequest().withSecretId(secretName));
                
                System.out.println("Retrieved secret from backup region: " + backupRegion);
                backupClient.shutdown();
                return result.getSecretString();
                
            } catch (Exception e) {
                System.err.println("Backup region " + backupRegion + " failed: " + e.getMessage());
            }
        }
        
        throw new RuntimeException("Secret not accessible from any region");
    }
}

Error Handling

Common replication-related exceptions:

try {
    ReplicateSecretToRegionsResult result = client.replicateSecretToRegions(request);
} catch (ResourceNotFoundException e) {
    System.err.println("Secret not found: " + e.getMessage());
} catch (InvalidParameterException e) {
    System.err.println("Invalid region or KMS key: " + e.getMessage());
} catch (LimitExceededException e) {
    System.err.println("Replication limit exceeded: " + e.getMessage());
} catch (ResourceExistsException e) {
    // Use forceOverwriteReplicaSecret if intentional
    System.err.println("Replica already exists: " + e.getMessage());
} catch (EncryptionFailureException e) {
    System.err.println("KMS encryption failed: " + e.getMessage());
} catch (AWSSecretsManagerException e) {
    System.err.println("Replication service error: " + e.getMessage());
}

Best Practices

  1. Region Selection: Choose regions close to your application deployments
  2. KMS Key Management: Use region-specific KMS keys for compliance
  3. Network Latency: Consider latency when accessing replicated secrets
  4. Cost Management: Monitor cross-region data transfer costs
  5. Failover Testing: Regularly test cross-region failover scenarios
  6. Monitoring: Set up CloudWatch alarms for replication failures
  7. Cleanup: Remove unnecessary replicas to reduce costs
  8. Security: Ensure IAM policies work across regions
  9. Consistency: Understand eventual consistency for cross-region updates
  10. Disaster Recovery: Plan for complete region failures

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