Skip navigation links

Package software.amazon.awscdk.services.secretsmanager

AWS Secrets Manager Construct Library

See: Description

Package software.amazon.awscdk.services.secretsmanager Description

AWS Secrets Manager Construct Library

---

cfn-resources: Stable

cdk-constructs: Stable


 import software.amazon.awscdk.services.secretsmanager.*;
 

Create a new Secret in a Stack

In order to have SecretsManager generate a new secret value automatically, you can get started with the following:

 // Default secret
 Secret secret = new Secret(this, "Secret");
 secret.grantRead(role);
 
 User.Builder.create(this, "User")
         .password(secret.getSecretValue())
         .build();
 
 // Templated secret
 Secret templatedSecret = Secret.Builder.create(this, "TemplatedSecret")
         .generateSecretString(SecretStringGenerator.builder()
                 .secretStringTemplate(JSON.stringify(Map.of("username", "user")))
                 .generateStringKey("password")
                 .build())
         .build();
 
 User.Builder.create(this, "OtherUser")
         .userName(templatedSecret.secretValueFromJson("username").toString())
         .password(templatedSecret.secretValueFromJson("password"))
         .build();
 

The Secret construct does not allow specifying the SecretString property of the AWS::SecretsManager::Secret resource (as this will almost always lead to the secret being surfaced in plain text and possibly committed to your source control).

If you need to use a pre-existing secret, the recommended way is to manually provision the secret in AWS SecretsManager and use the Secret.fromSecretArn or Secret.fromSecretAttributes method to make it available in your CDK Application:

 Key encryptionKey;
 
 ISecret secret = Secret.fromSecretAttributes(this, "ImportedSecret", SecretAttributes.builder()
         .secretArn("arn:aws:secretsmanager:<region>:<account-id-number>:secret:<secret-name>-<random-6-characters>")
         // If the secret is encrypted using a KMS-hosted CMK, either import or reference that key:
         .encryptionKey(encryptionKey)
         .build());
 

SecretsManager secret values can only be used in select set of properties. For the list of properties, see the CloudFormation Dynamic References documentation.

A secret can set RemovalPolicy. If it set to RETAIN, that removing a secret will fail.

Grant permission to use the secret to a role

You must grant permission to a resource for that resource to be allowed to use a secret. This can be achieved with the Secret.grantRead and/or Secret.grantUpdate method, depending on your need:

 Role role = Role.Builder.create(this, "SomeRole").assumedBy(new AccountRootPrincipal()).build();
 Secret secret = new Secret(this, "Secret");
 secret.grantRead(role);
 secret.grantWrite(role);
 

If, as in the following example, your secret was created with a KMS key:

 Role role;
 
 Key key = new Key(this, "KMS");
 Secret secret = Secret.Builder.create(this, "Secret").encryptionKey(key).build();
 secret.grantRead(role);
 secret.grantWrite(role);
 

then Secret.grantRead and Secret.grantWrite will also grant the role the relevant encrypt and decrypt permissions to the KMS key through the SecretsManager service principal.

The principal is automatically added to Secret resource policy and KMS Key policy for cross account access:

 AccountPrincipal otherAccount = new AccountPrincipal("1234");
 Key key = new Key(this, "KMS");
 Secret secret = Secret.Builder.create(this, "Secret").encryptionKey(key).build();
 secret.grantRead(otherAccount);
 

Rotating a Secret

Using a Custom Lambda Function

A rotation schedule can be added to a Secret using a custom Lambda function:

 import software.amazon.awscdk.services.lambda.*;
 
 Function fn;
 
 Secret secret = new Secret(this, "Secret");
 
 secret.addRotationSchedule("RotationSchedule", RotationScheduleOptions.builder()
         .rotationLambda(fn)
         .automaticallyAfter(Duration.days(15))
         .build());
 

Note: The required permissions for Lambda to call SecretsManager and the other way round are automatically granted based on AWS Documentation as long as the Lambda is not imported.

See Overview of the Lambda Rotation Function on how to implement a Lambda Rotation Function.

Using a Hosted Lambda Function

Use the hostedRotation prop to rotate a secret with a hosted Lambda function:

 Secret secret = new Secret(this, "Secret");
 
 secret.addRotationSchedule("RotationSchedule", RotationScheduleOptions.builder()
         .hostedRotation(HostedRotation.mysqlSingleUser())
         .build());
 

Hosted rotation is available for secrets representing credentials for MySQL, PostgreSQL, Oracle, MariaDB, SQLServer, Redshift and MongoDB (both for the single and multi user schemes).

When deployed in a VPC, the hosted rotation implements ec2.IConnectable:

 Vpc myVpc;
 Connections dbConnections;
 Secret secret;
 
 
 HostedRotation myHostedRotation = HostedRotation.mysqlSingleUser(SingleUserHostedRotationOptions.builder().vpc(myVpc).build());
 secret.addRotationSchedule("RotationSchedule", RotationScheduleOptions.builder().hostedRotation(myHostedRotation).build());
 dbConnections.allowDefaultPortFrom(myHostedRotation);
 

See also Automating secret creation in AWS CloudFormation.

Rotating database credentials

Define a SecretRotation to rotate database credentials:

 Secret mySecret;
 IConnectable myDatabase;
 Vpc myVpc;
 
 
 SecretRotation.Builder.create(this, "SecretRotation")
         .application(SecretRotationApplication.MYSQL_ROTATION_SINGLE_USER) // MySQL single user scheme
         .secret(mySecret)
         .target(myDatabase) // a Connectable
         .vpc(myVpc) // The VPC where the secret rotation application will be deployed
         .excludeCharacters(" %+:;{}")
         .build();
 

The secret must be a JSON string with the following format:

 {
   "engine": "<required: database engine>",
   "host": "<required: instance host name>",
   "username": "<required: username>",
   "password": "<required: password>",
   "dbname": "<optional: database name>",
   "port": "<optional: if not specified, default port will be used>",
   "masterarn": "<required for multi user rotation: the arn of the master secret which will be used to create users/change passwords>"
 }
 

For the multi user scheme, a masterSecret must be specified:

 Secret myUserSecret;
 Secret myMasterSecret;
 IConnectable myDatabase;
 Vpc myVpc;
 
 
 SecretRotation.Builder.create(this, "SecretRotation")
         .application(SecretRotationApplication.MYSQL_ROTATION_MULTI_USER)
         .secret(myUserSecret) // The secret that will be rotated
         .masterSecret(myMasterSecret) // The secret used for the rotation
         .target(myDatabase)
         .vpc(myVpc)
         .build();
 

See also aws-rds where credentials generation and rotation is integrated.

Importing Secrets

Existing secrets can be imported by ARN, name, and other attributes (including the KMS key used to encrypt the secret). Secrets imported by name should use the short-form of the name (without the SecretsManager-provided suffx); the secret name must exist in the same account and region as the stack. Importing by name makes it easier to reference secrets created in different regions, each with their own suffix and ARN.

 String secretCompleteArn = "arn:aws:secretsmanager:eu-west-1:111111111111:secret:MySecret-f3gDy9";
 String secretPartialArn = "arn:aws:secretsmanager:eu-west-1:111111111111:secret:MySecret"; // No Secrets Manager suffix
 IKey encryptionKey = Key.fromKeyArn(this, "MyEncKey", "arn:aws:kms:eu-west-1:111111111111:key/21c4b39b-fde2-4273-9ac0-d9bb5c0d0030");
 ISecret mySecretFromCompleteArn = Secret.fromSecretCompleteArn(this, "SecretFromCompleteArn", secretCompleteArn);
 ISecret mySecretFromPartialArn = Secret.fromSecretPartialArn(this, "SecretFromPartialArn", secretPartialArn);
 ISecret mySecretFromName = Secret.fromSecretNameV2(this, "SecretFromName", "MySecret");
 ISecret mySecretFromAttrs = Secret.fromSecretAttributes(this, "SecretFromAttributes", SecretAttributes.builder()
         .secretCompleteArn(secretCompleteArn)
         .encryptionKey(encryptionKey)
         .build());
 

Replicating secrets

Secrets can be replicated to multiple regions by specifying replicaRegions:

 Key myKey;
 
 Secret.Builder.create(this, "Secret")
         .replicaRegions(List.of(ReplicaRegion.builder()
                 .region("eu-west-1")
                 .build(), ReplicaRegion.builder()
                 .region("eu-central-1")
                 .encryptionKey(myKey)
                 .build()))
         .build();
 

Alternatively, use addReplicaRegion():

 Secret secret = new Secret(this, "Secret");
 secret.addReplicaRegion("eu-west-1");
 
Skip navigation links