AWS Secrets Manager Rotation in CloudFormation

I found AWS’s documentation for how to setup Secrets Manager secret rotation in CloudFormation to be severely lacking as no AWS documentation explains how to use the secret rotation templates provided by AWS within CloudFormation. Automating Secret Creation in AWS CloudFormation gives an example of how to setup the CloudFormation resources for the secret and its rotation, but it’s an incomplete example; it tells you where to place your Lambda’s S3 information in the template, but what if you want to use one of AWS’s provided rotation functions? AWS Templates You Can Use to Create Lambda Rotation Functions give you a list of RDS secret rotation “templates” and information about them but it does not give any information or examples for how to actually use them. After a bit of work, I figured out how to combine the information from those two documents to create a complete CloudFormation based RDS secret rotation example.

The AWS templates are Serverless Applications which can loaded in CloudFormation using AWS::Serverless::Application. To use them, start with the CloudFormation example given in Automating Secret Creation in AWS CloudFormation. Add the top level directive Transform: AWS::Serverless-2016-10-31 to allow processing of Serverless resources. Remove the MyRotationLambda resource. Add a new resource of type AWS::Serverless::Application referencing one of the rotation function listed in AWS Templates You Can Use to Create Lambda Rotation Functions. Then change LambdaInvokePermission to reference the Serverless Application. Here’s a complete example:

---
Description: "This is an example template to demonstrate CloudFormation resources for Secrets Manager"
Transform: AWS::Serverless-2016-10-31
Resources:

  #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: '"@/\'
      Tags:
        -
          Key: AppName
          Value: MyApp

  #This is a RDS instance resource. Its master username and password use dynamic references to resolve values from
  #SecretsManager. The dynamic reference guarantees that CloudFormation will not log or persist the resolved value
  #We use a ref to the Secret resource logical id in order to construct the dynamic reference, since the Secret name is being
  #generated by CloudFormation
  MyDBInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      AllocatedStorage: 20
      DBInstanceClass: db.t2.micro
      Engine: mysql
      MasterUsername: !Join ['', ['{{resolve:secretsmanager:', !Ref MyRDSInstanceRotationSecret, ':SecretString:username}}' ]]
      MasterUserPassword: !Join ['', ['{{resolve:secretsmanager:', !Ref MyRDSInstanceRotationSecret, ':SecretString: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 password for the referenced secret using a rotation lambda
  #The first rotation happens at resource creation time, with subsequent rotations scheduled according to the rotation rules
  #We explicitly depend on the SecretTargetAttachment resource being created 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 MyRotationServerlessApplication.Outputs.RotationLambdaARN
      RotationRules:
        AutomaticallyAfterDays: 30

  #This is ResourcePolicy resource which can be used to attach a resource policy to the referenced secret.
  #The resource policy in this example denies the DeleteSecret action to all principals within the current account
  MySecretResourcePolicy:
    Type: AWS::SecretsManager::ResourcePolicy
    Properties:
      SecretId: !Ref MyRDSInstanceRotationSecret
      ResourcePolicy: !Sub '{
                         "Version" : "2012-10-17",
                         "Statement" : [
                           {
                             "Effect": "Deny",
                             "Principal": {"AWS":"arn:aws:iam::${AWS::AccountId}:root"},
                             "Action": "secretsmanager:DeleteSecret",
                             "Resource": "*"
                           }
                         ]
                       }'

  #This is a Serverless Application resource. We will use its lambda to rotate secrets
  #For details about rotation lambdas, see https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets.html
  MyRotationServerlessApplication:
    Type: AWS::Serverless::Application
    Properties:
      Location:
        ApplicationId: arn:aws:serverlessrepo:us-east-1:297356227824:applications/SecretsManagerRDSMySQLRotationSingleUser
        SemanticVersion: 1.0.117
      Parameters:
        endpoint: !Sub 'https://secretsmanager.${AWS::Region}.${AWS::URLSuffix}'
        functionName: 'cfn-rotation-lambda'

  #This is a lambda Permission resource which grants Secrets Manager permission to invoke the rotation lambda function
  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    Properties:
      FunctionName: !GetAtt MyRotationServerlessApplication.Outputs.RotationLambdaARN
      Action: 'lambda:InvokeFunction'
      Principal: secretsmanager.amazonaws.com

This example uses SecretsManagerRDSMySQLRotationSingleUser which is the single user template for MySQL, however there are AWS provided templates for MariaDB, Oracle, and PostgreSQL as well. To find them, search the AWS Serverless Application Repository (the rotation templates all start with “SecretsManagerRDS”). Once you’ve found the desired template, click the “Deploy” button then select “Copy as SAM Resource” – the clipboard will now contain the CloudFormation YAML for the AWS::Serverless::Application resource.

CC BY-SA 4.0 AWS Secrets Manager Rotation in CloudFormation by Craig Andrews is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.

One thought on “AWS Secrets Manager Rotation in CloudFormation

  1. Great article,
    I hace found the article usefull. I was also searching for a bit more explanation for how to use the AWS provided lambda functions and didn’t find that in the AWS documentation.
    I did some testing with this and noticed that the lambda function also gets updated when I update the RDS instance through cloudformation. I haven’t found an explanation for that yet. Hope to find an answer for that soon.
    Regards

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.