Skip to content

Instantly share code, notes, and snippets.

@pgressa
Last active November 18, 2021 21:16
Show Gist options
  • Save pgressa/2340592195722081ee41601a0439274c to your computer and use it in GitHub Desktop.
Save pgressa/2340592195722081ee41601a0439274c to your computer and use it in GitHub Desktop.
cloudformation for rds and autorotate
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::SecretsManager-2020-07-23
Description: Deploys an RDS instance
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Basic configuration
Parameters:
- DBName
- DBInstanceClass
- DBAllocatedStorage
- CidrIp
ParameterLabels:
DBName:
default: Database name
DBInstanceClass:
default: Database instance class
DBAllocatedStorage:
default: The size of the database (GiB)
CidrIp:
default: CIDR block for the ingress security group
Parameters:
DBName:
Default: dbname
Description: Database name
Type: String
MinLength: '1'
MaxLength: '64'
AllowedPattern: '[a-zA-Z][a-zA-Z0-9]*'
ConstraintDescription: Must begin with a letter and contain only alphanumeric characters.
DBInstanceClass:
Default: db.m5.large
Description: DB instance class
Type: String
ConstraintDescription: Must select a valid DB instance type.
DBAllocatedStorage:
Default: '50'
Description: The size of the database (GiB)
Type: Number
MinValue: '5'
MaxValue: '1024'
ConstraintDescription: must be between 20 and 65536 GiB.
CidrIp:
AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/(0|1[6-9]|2[0-8]))$
ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16–28
Default: 10.0.0.0/16
Description: CIDR block for the ingress security group.
Type: String
Resources:
# Inspiration https://docs.aws.amazon.com/secretsmanager/latest/userguide/integrating_cloudformation.html
# RDS VPC
RdsVpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.0.0.0/16
EnableDnsHostnames: true
EnableDnsSupport: true
# Subnet that the rotation Lambda function and the RDS instance will be placed in
RdsSubnet01:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.96.0/19
AvailabilityZone:
Fn::Select:
- '0'
- Fn::GetAZs: { Ref: 'AWS::Region' }
VpcId:
Ref: RdsVpc
RdsSubnet02:
Type: AWS::EC2::Subnet
Properties:
CidrBlock: 10.0.128.0/19
AvailabilityZone:
Fn::Select:
- '1'
- Fn::GetAZs: {Ref: 'AWS::Region'}
VpcId:
Ref: RdsVpc
# VPC endpoint that will enable the rotation Lambda function to make api calls to Secrets Manager
SecretsManagerVPCEndpoint:
Type: AWS::EC2::VPCEndpoint
Properties:
SubnetIds:
- Ref: RdsSubnet01
- Ref: RdsSubnet02
SecurityGroupIds:
- !GetAtt RdsVpc.DefaultSecurityGroup
VpcEndpointType: 'Interface'
ServiceName: !Sub "com.amazonaws.${AWS::Region}.secretsmanager"
PrivateDnsEnabled: true
VpcId:
Ref: RdsVpc
# This is a Secret resource with a randomly generated password in its SecretString JSON.
RDSInstanceRotationSecret:
Type: AWS::SecretsManager::Secret
Properties:
GenerateSecretString:
SecretStringTemplate: '{"username": "admin"}'
GenerateStringKey: 'password'
PasswordLength: 16
ExcludeCharacters: '"@/\'
# This is an 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 sub the Secret resource's logical id in order to construct the dynamic reference, since the Secret's name is being #generated by CloudFormation
DBInstance:
Type: AWS::RDS::DBInstance
Properties:
AllocatedStorage: !Ref DBAllocatedStorage
DBInstanceClass: !Ref DBInstanceClass
Engine: mysql
DBSubnetGroupName:
Ref: DBSubnetGroup
MasterUsername: !Sub '{{resolve:secretsmanager:${RDSInstanceRotationSecret}::username}}'
MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSInstanceRotationSecret}::password}}'
BackupRetentionPeriod: 0
VPCSecurityGroups:
- !GetAtt RdsVpc.DefaultSecurityGroup
- Ref: EksSecurityGroup
DBName: !Ref DBName
# Database subnet group for the RDS instance
DBSubnetGroup:
Type: AWS::RDS::DBSubnetGroup
Properties:
DBSubnetGroupDescription: "DB Subnet group"
SubnetIds:
- Ref: RdsSubnet01
- Ref: RdsSubnet02
# 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 RDSInstanceRotationSecret
TargetId: !Ref DBInstance
TargetType: AWS::RDS::DBInstance
# This is a RotationSchedule resource. It configures rotation of password for the referenced secret using a rotation lambda function
# 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
SecretRotationSchedule:
Type: AWS::SecretsManager::RotationSchedule
DependsOn: SecretRDSInstanceAttachment
Properties:
SecretId: !Ref RDSInstanceRotationSecret
HostedRotationLambda:
RotationType: MySQLSingleUser
RotationLambdaName: SecretsManagerRotation
VpcSecurityGroupIds: !GetAtt RdsVpc.DefaultSecurityGroup
VpcSubnetIds:
Fn::Join:
- ","
- - Ref: RdsSubnet01
- Ref: RdsSubnet02
RotationRules:
AutomaticallyAfterDays: 2
EksSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Open database for access
VpcId: !Ref RdsVpc
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: '3306'
ToPort: '3306'
CidrIp:
Ref: CidrIp
Outputs:
DbPort:
Value: !GetAtt DBInstance.Endpoint.Port
DbAddress:
Value: !GetAtt DBInstance.Endpoint.Address
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment