0
# Rotation Management
1
2
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.
3
4
## Rotation Concepts
5
6
- **AWSCURRENT**: The current active version of the secret
7
- **AWSPENDING**: The new version being created during rotation
8
- **Rotation Lambda**: AWS Lambda function that performs the actual credential rotation
9
- **Rotation Rules**: Configuration specifying when rotation should occur
10
11
## Core Rotation Operations
12
13
### Starting Rotation
14
15
#### RotateSecretRequest
16
17
```java { .api }
18
class RotateSecretRequest extends AmazonWebServiceRequest {
19
String secretId; // Required: Secret identifier
20
String clientRequestToken; // Idempotency token
21
String rotationLambdaARN; // Lambda function ARN for rotation
22
RotationRulesType rotationRules; // Rotation schedule configuration
23
Boolean rotateImmediately; // Start rotation immediately
24
Boolean forceRotateSecretVersion; // Force rotation even if AWSPENDING exists
25
}
26
```
27
28
#### RotateSecretResult
29
30
```java { .api }
31
class RotateSecretResult {
32
String arn; // Secret ARN
33
String name; // Secret name
34
String versionId; // Version ID of AWSPENDING version
35
}
36
```
37
38
#### RotationRulesType
39
40
```java { .api }
41
class RotationRulesType {
42
Long automaticallyAfterDays; // Number of days between rotations (1-1000)
43
}
44
```
45
46
#### Usage Example
47
48
```java { .api }
49
// Set up automatic rotation for database credentials
50
RotationRulesType rotationRules = new RotationRulesType()
51
.withAutomaticallyAfterDays(30L);
52
53
RotateSecretRequest rotateRequest = new RotateSecretRequest()
54
.withSecretId("rds/prod/masteruser")
55
.withRotationLambdaARN("arn:aws:lambda:us-west-2:123456789012:function:SecretsManagerRotation")
56
.withRotationRules(rotationRules)
57
.withRotateImmediately(true);
58
59
RotateSecretResult rotateResult = client.rotateSecret(rotateRequest);
60
System.out.println("Started rotation for secret: " + rotateResult.getName());
61
System.out.println("AWSPENDING version: " + rotateResult.getVersionId());
62
63
// Start immediate rotation without changing schedule
64
RotateSecretRequest immediateRotateRequest = new RotateSecretRequest()
65
.withSecretId("api/service/token")
66
.withRotateImmediately(true);
67
68
RotateSecretResult immediateResult = client.rotateSecret(immediateRotateRequest);
69
```
70
71
### Canceling Rotation
72
73
#### CancelRotateSecretRequest
74
75
```java { .api }
76
class CancelRotateSecretRequest extends AmazonWebServiceRequest {
77
String secretId; // Required: Secret identifier
78
}
79
```
80
81
#### CancelRotateSecretResult
82
83
```java { .api }
84
class CancelRotateSecretResult {
85
String arn; // Secret ARN
86
String name; // Secret name
87
String versionId; // Version ID of cancelled rotation
88
}
89
```
90
91
#### Usage Example
92
93
```java { .api }
94
// Cancel an in-progress rotation
95
CancelRotateSecretRequest cancelRequest = new CancelRotateSecretRequest()
96
.withSecretId("rds/prod/masteruser");
97
98
try {
99
CancelRotateSecretResult cancelResult = client.cancelRotateSecret(cancelRequest);
100
System.out.println("Cancelled rotation for: " + cancelResult.getName());
101
} catch (InvalidRequestException e) {
102
System.err.println("No rotation in progress or cannot cancel: " + e.getMessage());
103
}
104
```
105
106
## Rotation Configuration
107
108
### Configuring Rotation During Secret Creation
109
110
```java { .api }
111
// Create secret with rotation configuration
112
RotationRulesType rotationRules = new RotationRulesType()
113
.withAutomaticallyAfterDays(45L);
114
115
CreateSecretRequest createWithRotation = new CreateSecretRequest()
116
.withName("database/credentials")
117
.withSecretString("{\"username\":\"admin\",\"password\":\"initialPassword\"}")
118
.withDescription("Database credentials with automatic rotation");
119
120
// First create the secret
121
CreateSecretResult createResult = client.createSecret(createWithRotation);
122
123
// Then configure rotation
124
RotateSecretRequest configureRotation = new RotateSecretRequest()
125
.withSecretId(createResult.getName())
126
.withRotationLambdaARN("arn:aws:lambda:us-west-2:123456789012:function:DatabaseRotation")
127
.withRotationRules(rotationRules);
128
129
RotateSecretResult rotationConfig = client.rotateSecret(configureRotation);
130
```
131
132
### Updating Rotation Configuration
133
134
```java { .api }
135
// Update rotation schedule
136
RotationRulesType newRules = new RotationRulesType()
137
.withAutomaticallyAfterDays(60L); // Change from 30 to 60 days
138
139
RotateSecretRequest updateRotation = new RotateSecretRequest()
140
.withSecretId("rds/prod/masteruser")
141
.withRotationRules(newRules);
142
143
RotateSecretResult updateResult = client.rotateSecret(updateRotation);
144
145
// Change rotation Lambda function
146
RotateSecretRequest updateLambda = new RotateSecretRequest()
147
.withSecretId("api/credentials")
148
.withRotationLambdaARN("arn:aws:lambda:us-west-2:123456789012:function:NewRotationFunction");
149
150
RotateSecretResult lambdaUpdate = client.rotateSecret(updateLambda);
151
```
152
153
## Version Stage Management
154
155
### Understanding Version Stages
156
157
- **AWSCURRENT**: The currently active secret version
158
- **AWSPENDING**: A new version being created during rotation
159
- **Custom Stages**: User-defined stages for specific use cases
160
161
### Moving Version Stages
162
163
#### UpdateSecretVersionStageRequest
164
165
```java { .api }
166
class UpdateSecretVersionStageRequest extends AmazonWebServiceRequest {
167
String secretId; // Required: Secret identifier
168
String versionStage; // Required: Stage to move
169
String clientRequestToken; // Idempotency token
170
String moveToVersionId; // Version to move stage to
171
String removeFromVersionId; // Version to remove stage from
172
}
173
```
174
175
#### UpdateSecretVersionStageResult
176
177
```java { .api }
178
class UpdateSecretVersionStageResult {
179
String arn; // Secret ARN
180
String name; // Secret name
181
}
182
```
183
184
#### Usage Example
185
186
```java { .api }
187
// Promote AWSPENDING to AWSCURRENT (complete rotation)
188
UpdateSecretVersionStageRequest promoteRequest = new UpdateSecretVersionStageRequest()
189
.withSecretId("rds/prod/masteruser")
190
.withVersionStage("AWSCURRENT")
191
.withMoveToVersionId("new-version-id")
192
.withRemoveFromVersionId("old-version-id");
193
194
UpdateSecretVersionStageResult promoteResult = client.updateSecretVersionStage(promoteRequest);
195
196
// Create custom stage for testing
197
UpdateSecretVersionStageRequest testStageRequest = new UpdateSecretVersionStageRequest()
198
.withSecretId("api/credentials")
199
.withVersionStage("TESTING")
200
.withMoveToVersionId("test-version-id");
201
202
UpdateSecretVersionStageResult testResult = client.updateSecretVersionStage(testStageRequest);
203
204
// Rollback by moving AWSCURRENT to previous version
205
UpdateSecretVersionStageRequest rollbackRequest = new UpdateSecretVersionStageRequest()
206
.withSecretId("database/password")
207
.withVersionStage("AWSCURRENT")
208
.withMoveToVersionId("previous-version-id")
209
.withRemoveFromVersionId("current-version-id");
210
211
UpdateSecretVersionStageResult rollbackResult = client.updateSecretVersionStage(rollbackRequest);
212
```
213
214
## Rotation Monitoring
215
216
### Checking Rotation Status
217
218
```java { .api }
219
// Check rotation status using describe
220
DescribeSecretRequest statusRequest = new DescribeSecretRequest()
221
.withSecretId("rds/prod/masteruser");
222
223
DescribeSecretResult statusResult = client.describeSecret(statusRequest);
224
225
System.out.println("Rotation Enabled: " + statusResult.getRotationEnabled());
226
System.out.println("Rotation Lambda: " + statusResult.getRotationLambdaARN());
227
System.out.println("Last Rotated: " + statusResult.getLastRotatedDate());
228
System.out.println("Next Rotation: " + statusResult.getNextRotationDate());
229
230
if (statusResult.getRotationRules() != null) {
231
Long days = statusResult.getRotationRules().getAutomaticallyAfterDays();
232
System.out.println("Rotation Interval: " + days + " days");
233
}
234
235
// Check version stages to see rotation progress
236
Map<String, List<String>> versionStages = statusResult.getVersionIdsToStages();
237
for (Map.Entry<String, List<String>> entry : versionStages.entrySet()) {
238
String versionId = entry.getKey();
239
List<String> stages = entry.getValue();
240
241
if (stages.contains("AWSPENDING")) {
242
System.out.println("Rotation in progress - AWSPENDING version: " + versionId);
243
}
244
if (stages.contains("AWSCURRENT")) {
245
System.out.println("Current active version: " + versionId);
246
}
247
}
248
```
249
250
### Handling Rotation States
251
252
```java { .api }
253
public class RotationStatusChecker {
254
255
public enum RotationState {
256
NOT_CONFIGURED,
257
CONFIGURED_NOT_ROTATING,
258
ROTATION_IN_PROGRESS,
259
ROTATION_FAILED
260
}
261
262
public RotationState checkRotationState(AWSSecretsManager client, String secretId) {
263
try {
264
DescribeSecretResult result = client.describeSecret(
265
new DescribeSecretRequest().withSecretId(secretId));
266
267
// Check if rotation is configured
268
if (!Boolean.TRUE.equals(result.getRotationEnabled())) {
269
return RotationState.NOT_CONFIGURED;
270
}
271
272
// Check for AWSPENDING version
273
Map<String, List<String>> versions = result.getVersionIdsToStages();
274
boolean hasPending = versions.values().stream()
275
.anyMatch(stages -> stages.contains("AWSPENDING"));
276
277
if (hasPending) {
278
// Check if rotation is stuck (older than expected)
279
Date lastRotated = result.getLastRotatedDate();
280
long hoursSinceRotation = (System.currentTimeMillis() -
281
lastRotated.getTime()) / (1000 * 60 * 60);
282
283
if (hoursSinceRotation > 24) { // Rotation taking too long
284
return RotationState.ROTATION_FAILED;
285
}
286
return RotationState.ROTATION_IN_PROGRESS;
287
}
288
289
return RotationState.CONFIGURED_NOT_ROTATING;
290
291
} catch (Exception e) {
292
System.err.println("Error checking rotation state: " + e.getMessage());
293
return RotationState.ROTATION_FAILED;
294
}
295
}
296
}
297
```
298
299
## Advanced Rotation Scenarios
300
301
### Database Credential Rotation
302
303
```java { .api }
304
// Rotate RDS database master password
305
public void rotateRDSMasterPassword(String secretId, String dbInstanceId) {
306
// The rotation Lambda function will:
307
// 1. Create new password in AWSPENDING version
308
// 2. Update RDS master password
309
// 3. Test new credentials
310
// 4. Move AWSCURRENT stage to new version
311
312
RotateSecretRequest rotateRequest = new RotateSecretRequest()
313
.withSecretId(secretId)
314
.withRotationLambdaARN("arn:aws:lambda:region:account:function:SecretsManagerRDSMySQLRotationSingleUser")
315
.withRotateImmediately(true);
316
317
try {
318
RotateSecretResult result = client.rotateSecret(rotateRequest);
319
System.out.println("Started RDS rotation: " + result.getVersionId());
320
321
// Wait for rotation to complete
322
waitForRotationCompletion(secretId);
323
324
} catch (Exception e) {
325
System.err.println("Rotation failed: " + e.getMessage());
326
}
327
}
328
329
private void waitForRotationCompletion(String secretId) {
330
int maxWaitMinutes = 15;
331
int waitIntervalSeconds = 30;
332
int attempts = (maxWaitMinutes * 60) / waitIntervalSeconds;
333
334
for (int i = 0; i < attempts; i++) {
335
try {
336
DescribeSecretResult result = client.describeSecret(
337
new DescribeSecretRequest().withSecretId(secretId));
338
339
Map<String, List<String>> versions = result.getVersionIdsToStages();
340
boolean hasPending = versions.values().stream()
341
.anyMatch(stages -> stages.contains("AWSPENDING"));
342
343
if (!hasPending) {
344
System.out.println("Rotation completed successfully");
345
return;
346
}
347
348
Thread.sleep(waitIntervalSeconds * 1000);
349
350
} catch (InterruptedException e) {
351
Thread.currentThread().interrupt();
352
break;
353
} catch (Exception e) {
354
System.err.println("Error checking rotation status: " + e.getMessage());
355
}
356
}
357
358
System.err.println("Rotation did not complete within expected time");
359
}
360
```
361
362
### Multi-User Rotation
363
364
```java { .api }
365
// Rotate credentials for multi-user scenarios (alternating users)
366
public void rotateMultiUserCredentials(String secretId) {
367
RotateSecretRequest rotateRequest = new RotateSecretRequest()
368
.withSecretId(secretId)
369
.withRotationLambdaARN("arn:aws:lambda:region:account:function:SecretsManagerRDSMySQLRotationMultiUser")
370
.withRotationRules(new RotationRulesType().withAutomaticallyAfterDays(30L))
371
.withRotateImmediately(true);
372
373
RotateSecretResult result = client.rotateSecret(rotateRequest);
374
System.out.println("Started multi-user rotation: " + result.getVersionId());
375
}
376
```
377
378
### Custom Application Rotation
379
380
```java { .api }
381
// Rotate API keys or application-specific credentials
382
public void rotateAPIKey(String secretId, String apiEndpoint) {
383
RotateSecretRequest rotateRequest = new RotateSecretRequest()
384
.withSecretId(secretId)
385
.withRotationLambdaARN("arn:aws:lambda:region:account:function:CustomAPIKeyRotation")
386
.withRotateImmediately(true);
387
388
try {
389
RotateSecretResult result = client.rotateSecret(rotateRequest);
390
System.out.println("Started API key rotation: " + result.getVersionId());
391
392
// The Lambda function should:
393
// 1. Generate new API key via the service API
394
// 2. Store new key in AWSPENDING version
395
// 3. Test new key works
396
// 4. Optionally revoke old key
397
// 5. Move AWSCURRENT stage to new version
398
399
} catch (Exception e) {
400
System.err.println("API key rotation failed: " + e.getMessage());
401
}
402
}
403
```
404
405
## Error Handling
406
407
Common rotation-related exceptions:
408
409
```java { .api }
410
try {
411
RotateSecretResult result = client.rotateSecret(rotateRequest);
412
} catch (ResourceNotFoundException e) {
413
System.err.println("Secret not found: " + e.getMessage());
414
} catch (InvalidParameterException e) {
415
System.err.println("Invalid rotation configuration: " + e.getMessage());
416
} catch (InvalidRequestException e) {
417
System.err.println("Rotation cannot be performed: " + e.getMessage());
418
} catch (LimitExceededException e) {
419
System.err.println("Too many rotation requests: " + e.getMessage());
420
} catch (PreconditionNotMetException e) {
421
System.err.println("Rotation precondition failed: " + e.getMessage());
422
} catch (AWSSecretsManagerException e) {
423
System.err.println("Rotation service error: " + e.getMessage());
424
}
425
426
// Handle rotation cancellation errors
427
try {
428
CancelRotateSecretResult result = client.cancelRotateSecret(cancelRequest);
429
} catch (InvalidRequestException e) {
430
// Common reasons:
431
// - No rotation in progress
432
// - Rotation too far along to cancel
433
// - Lambda function already executing
434
System.err.println("Cannot cancel rotation: " + e.getMessage());
435
}
436
```
437
438
## Best Practices
439
440
1. **Lambda Function Setup**: Ensure rotation Lambda has proper IAM permissions
441
2. **Testing**: Test rotation in non-production environments first
442
3. **Monitoring**: Set up CloudWatch alarms for rotation failures
443
4. **Rollback Plan**: Know how to rollback using version stage management
444
5. **Network Access**: Ensure Lambda can reach both Secrets Manager and target service
445
6. **Error Handling**: Implement proper retry logic for transient failures
446
7. **Scheduling**: Choose rotation intervals based on security requirements
447
8. **Multi-Region**: Consider cross-region implications for replicated secrets