AWS::SecretsManager::RotationSchedule
The AWS::SecretsManager::RotationSchedule resource configures rotation for a
secret. The secret must already be configured with the details of the database or
service. If
you define both the secret and the database or service in a AWS CloudFormation template,
then define the
AWS::SecretsManager::SecretTargetAttachment resource to populate the
secret with the connection details of the database or service before you attempt to
configure
rotation.
Important
When you configure rotation for a secret, Secrets Manager automatically rotates the secret one time. Ensure that you configure all your clients to retrieve the secret using Secrets Manager before configuring rotation to prevent breaking them.
Topics
Syntax
To declare this entity in your AWS CloudFormation template, use the following syntax:
JSON
{ "Type" : "AWS::SecretsManager::RotationSchedule", "Properties" : { "SecretId" :String, "RotationLambdaARN" :String, "RotationRules" : RotationRules } }
YAML
Type: "AWS::SecretsManager::RotationSchedule" Properties: SecretId:StringRotationLambdaARN:StringRotationRules: RotationRules
Properties
SecretId-
Specifies the Amazon Resource Name (ARN) or the friendly name of the secret that you want to rotate. To reference a secret also that's created in this template, use the Ref function with the secret's logical ID.
Required: Yes
Type: String
Update requires: Replacement
RotationLambdaARN-
Specifies the ARN of the Lambda function that can rotate the secret. If you don't specify this parameter, then the secret must already have the ARN of a Lambda function configured. To reference a Lambda function that's also created in this template, use the Ref function with the function's logical ID.
Required: No
Type: String
Update requires: No interruption
RotationRules-
Specifies a structure that defines the rotation schedule for this secret.
Required: No
Type: Secrets Manager RotationSchedule RotationRules
Update requires: No interruption
Return Values
Ref
When you pass the logical ID of an AWS::SecretsManager::RotationSchedule
resource to the intrinsic Ref function, the function returns the ARN of the
secret that's being configured, such as:
arn:aws:secretsmanager:us-west-2:123456789012:secret:my-path/my-secret-name-1a2b3c
This enables you to reference a secret that you create in one part of the stack template from within the definition of another resource later, in the same template. You typically do this when you define the AWS::SecretsManager::SecretTargetAttachment resource type.
For more information about using the Ref function, see Ref.
Fn::GetAtt
Fn::GetAtt returns a value for a specified attribute of this type. This
section lists the available attributes and a sample return value.
VersionId-
The unique version ID of the new version of the secret created by the initial rotation that's triggered by this request.
Examples
Configuring a Secret to Rotate
The following example shows a complete example of creating a secret, creating an RDS DB instance that's associated with the secret, and configuring it to rotate. It shows how to define a Lambda rotation function, attach the required trust and permissions policies, and then associate the function with the secret on a defined schedule.
Note
The JSON specification doesn't allow any kind of comments. See the YAML example for comments.
JSON
{ "MyRDSInstanceRotationSecret": { "Type": "AWS::SecretsManager::Secret", "Properties": { "Description": "This is my rds instance secret", "GenerateSecretString": { "SecretStringTemplate": "{\"username\": \"admin\"}", "GenerateStringKey": "password", "PasswordLength": 16, "ExcludeCharacters": "\"@/\\" } } }, "SecretRDSInstanceAttachment": { "Type": "AWS::SecretsManager::SecretTargetAttachment", "Properties": { "SecretId": {"Ref": "MyRDSInstanceRotationSecret"}, "TargetId": {"Ref": "MyDBInstance"}, "TargetType": "AWS::RDS::DBInstance" } }, "MyDBInstance": { "Type": "AWS::RDS::DBInstance", "Properties": { "AllocatedStorage": "’20’", "DBInstanceClass": "db.t2.micro", "Engine": "mysql", "MasterUsername": {"Fn::Join": [ "", ["{{resolve:secretsmanager:",{"Ref": "MyRDSInstanceRotationSecret"},"::username}}"] ]}, "MasterUserPassword": {"Fn::Join": [ "", ["{{resolve:secretsmanager:",{"Ref": "MyRDSInstanceRotationSecret"},"::password}}"] ]}, "BackupRetentionPeriod": 0, "DBInstanceIdentifier": "rotation-instance" } }, "MySecretRotationSchedule": { "Type": "“AWS::SecretsManager::RotationSchedule\"", "DependsOn": "SecretRDSInstanceAttachment", "Properties": { "SecretId": {"Ref": "MyRDSInstanceRotationSecret"}, "RotationLambdaARN": {"Fn::GetAtt": ["MyRotationLambda", "Arn"]}, "RotationRules": { "AutomaticallyAfterDays": 5 } } }, "LambdaInvokePermission": { "Type": "AWS::Lambda::Permission", "DependsOn": "MyRotationLambda", "Properties": { "FunctionName": "cfn-rotation-lambda", "Action": "lambda:InvokeFunction", "Principal": "secretsmanager.amazonaws.com" } }, "MyLambdaExecutionRole": { "Type": "AWS::IAM::Role", "Properties": { "RoleName": "cfn-rotation-lambda-role", "AssumeRolePolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": [ "lambda.amazonaws.com" ] }, "Action": [ "sts:AssumeRole" ] } ] }, "Policies": [ { "PolicyName": "AWSSecretsManagerRotationPolicy", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "secretsmanager:DescribeSecret", "secretsmanager:GetSecretValue", "secretsmanager:PutSecretValue", "secretsmanager:UpdateSecretVersionStage" ], "Resource": {"Fn::Sub": "arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*"}, "Condition": { "StringEquals": { "secretsmanager:resource/AllowRotationLambdaArn": { "Fn::Sub": "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:cfn-rotation-lambda" } } } }, { "Effect": "Allow", "Action": [ "secretsmanager:GetRandomPassword" ], "Resource": "*" }, { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "kms:Decrypt", "kms:DescribeKey", "kms:GenerateDataKey" ], "Resource": "*" } ] } } ] } }, "MyRotationLambda": { "Type": "AWS::Lambda::Function", "Properties": { "Runtime": "python2.7", "Role": {"Fn::GetAtt": ["MyLambdaExecutionRole","Arn"]}, "Handler": "lambda_function.lambda_handler", "Description": "This is a lambda to rotate MySql user passwd", "FunctionName": "cfn-rotation-lambda", "Environment": { "Variables": { "SECRETS_MANAGER_ENDPOINT": {"Fn::Sub": "https://secretsmanager.${AWS::Region}.amazonaws.com"} } }, "Code": { "S3Bucket": "<% replace-this-with-name-of-s3-bucket-that-contains-lambda-function-code %>", "S3Key": "<% replace-this-with-path-and-filename-of-zip-file-that-contains-lambda-function-code %>", "S3ObjectVersion": "<% replace-this-with-lambda-zip-file-version-if-s3-bucket-versioning-is-enabled %>" } } } }
YAML
#This is a Secret resource with a randomly generated password in its SecretString JSON. MyRDSInstanceRotationSecret: Type: AWS::SecretsManager::Secret Properties: Description: "This is my rds instance secret" GenerateSecretString: SecretStringTemplate: '{"username": "admin"}' GenerateStringKey: "password" PasswordLength: 16 ExcludeCharacters: '"@/\' # This is an RDS instance resource. The master username and password use dynamic references # to resolve values from Secrets Manager. The dynamic reference guarantees that CloudFormation # will not log or persist the resolved value. We use a Ref to the secret resource's logical id # to construct the dynamic reference, since the secret name is generated by CloudFormation. MyDBInstance: Type: AWS::RDS::DBInstance Properties: AllocatedStorage: 20 DBInstanceClass: db.t2.micro Engine: mysql MasterUsername: !Join ['', ['{{resolve:secretsmanager:', !Ref MyRDSInstanceRotationSecret, '::username}}' ]] MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref MyRDSInstanceRotationSecret, '::password}}' ]] BackupRetentionPeriod: 0 DBInstanceIdentifier: 'rotation-instance' #This is a SecretTargetAttachment resource which updates the referenced Secret resource with properties about #the referenced RDS instance SecretRDSInstanceAttachment: Type: AWS::SecretsManager::SecretTargetAttachment Properties: SecretId: !Ref MyRDSInstanceRotationSecret TargetId: !Ref MyDBInstance TargetType: AWS::RDS::DBInstance # This is a RotationSchedule resource. It configures rotation of the password for the referenced # secret using a Lambda rotation function. The first rotation happens immediately when # CloudFormation processes this resource type. All subsequent rotations are scheduled according to # the configured rotation rules. We explicitly depend on the SecretTargetAttachment resource to # ensure that the secret contains all the information necessary for rotation to succeed. MySecretRotationSchedule: Type: AWS::SecretsManager::RotationSchedule DependsOn: SecretRDSInstanceAttachment Properties: SecretId: !Ref MyRDSInstanceRotationSecret RotationLambdaARN: !GetAtt MyRotationLambda.Arn RotationRules: AutomaticallyAfterDays: 5 #This is a Lambda Permission resource that grants Secrets Manager permission to invoke the function LambdaInvokePermission: Type: AWS::Lambda::Permission DependsOn: MyRotationLambda Properties: FunctionName: 'cfn-rotation-lambda' Action: 'lambda:InvokeFunction' Principal: secretsmanager.amazonaws.com # This is an IAM Role resource. It gets attached to the Lambda rotation function and grants # permissions to the function to retrieve and update the secret as part of the rotation process. # It also includes the required KMS permissions and CloudWatch logging permissions. MyLambdaExecutionRole: Type: AWS::IAM::Role Properties: RoleName: 'cfn-rotation-lambda-role' AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Principal: Service: - "lambda.amazonaws.com" Action: - "sts:AssumeRole" Policies: - PolicyName: "AWSSecretsManagerRotationPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - "secretsmanager:DescribeSecret" - "secretsmanager:GetSecretValue" - "secretsmanager:PutSecretValue" - "secretsmanager:UpdateSecretVersionStage" Resource: !Sub 'arn:${AWS::Partition}:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:*' Condition: StringEquals: secretsmanager:resource/AllowRotationLambdaArn: !Sub 'arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:cfn-rotation-lambda' - Effect: "Allow" Action: - "secretsmanager:GetRandomPassword" Resource: "*" - Effect: "Allow" Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: "arn:aws:logs:*:*:*" - Effect: "Allow" Action: - "kms:Decrypt" - "kms:DescribeKey" - "kms:GenerateDataKey" Resource: "*" # This is a Lambda Function resource. We use this to create the rotation function that rotate # our secret. For details about rotation Lambdas, see: # https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets.html # This example assumes that the Lambda code is already present in an S3 bucket, and that it # is coded to rotate a MySQL database password MyRotationLambda: Type: AWS::Lambda::Function Properties: Runtime: python2.7 Role: !GetAtt MyLambdaExecutionRole.Arn Handler: lambda_function.lambda_handler Description: 'This is a lambda to rotate MySql user passwd' FunctionName: 'cfn-rotation-lambda' Environment: Variables: SECRETS_MANAGER_ENDPOINT: !Sub 'https://secretsmanager.${AWS::Region}.amazonaws.com' Code: S3Bucket: <% replace-this-with-name-of-s3-bucket-that-contains-lambda-function-code %> S3Key: <% replace-this-with-path-and-filename-of-zip-file-that-contains-lambda-function-code %> S3ObjectVersion: <% replace-this-with-lambda-zip-file-version-if-s3-bucket-versioning-is-enabled %>
See Also
-
RotateSecret in the AWS Secrets Manager API Reference
-
Rotating Your AWS Secrets Manager Secrets in the AWS Secrets Manager User Guide
