Claude-skill-registry aws-cloudformation-rds
AWS CloudFormation patterns for Amazon RDS databases. Use when creating RDS instances (MySQL, PostgreSQL, Aurora), DB clusters, multi-AZ deployments, parameter groups, subnet groups, and implementing template structure with Parameters, Outputs, Mappings, Conditions, and cross-stack references.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/aws-cloudformation-rds" ~/.claude/skills/majiayu000-claude-skill-registry-aws-cloudformation-rds && rm -rf "$T"
skills/data/aws-cloudformation-rds/SKILL.mdAWS CloudFormation RDS Database
Overview
Create production-ready Amazon RDS infrastructure using AWS CloudFormation templates. This skill covers RDS instances (MySQL, PostgreSQL, Aurora, MariaDB), DB clusters, multi-AZ deployments, parameter groups, subnet groups, security groups, template structure best practices, parameter patterns, and cross-stack references for modular, reusable infrastructure as code.
When to Use
Use this skill when:
- Creating new RDS database instances (MySQL, PostgreSQL, Aurora, MariaDB)
- Configuring DB clusters with read replicas
- Setting up multi-AZ deployments for high availability
- Creating DB parameter groups and option groups
- Configuring DB subnet groups for VPC deployment
- Implementing template Parameters with AWS-specific types
- Creating Outputs for cross-stack references
- Organizing templates with Mappings and Conditions
- Designing reusable, modular CloudFormation templates
- Integrating with Secrets Manager for credential management
Quick Start
Basic MySQL RDS Instance
AWSTemplateFormatVersion: 2010-09-09 Description: Simple MySQL RDS instance with basic configuration Parameters: DBInstanceIdentifier: Type: String Default: mydatabase Description: Database instance identifier MasterUsername: Type: String Default: admin Description: Master username MasterUserPassword: Type: String NoEcho: true Description: Master user password DBInstanceClass: Type: String Default: db.t3.micro AllowedValues: - db.t3.micro - db.t3.small - db.t3.medium Resources: DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnet group for RDS SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 DBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: !Ref DBInstanceIdentifier DBInstanceClass: !Ref DBInstanceClass Engine: mysql MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword DBSubnetGroupName: !Ref DBSubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup AllocatedStorage: "20" StorageType: gp3 MultiAZ: false Outputs: DBInstanceEndpoint: Description: Database endpoint address Value: !GetAtt DBInstance.Endpoint.Address DBInstancePort: Description: Database port Value: !GetAtt DBInstance.Endpoint.Port
Aurora MySQL Cluster
AWSTemplateFormatVersion: 2010-09-09 Description: Aurora MySQL cluster with writer and reader instances Parameters: DBClusterIdentifier: Type: String Default: my-aurora-cluster Description: Cluster identifier MasterUsername: Type: String Default: admin MasterUserPassword: Type: String NoEcho: true Resources: DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnet group for Aurora SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 DBCluster: Type: AWS::RDS::DBCluster Properties: DBClusterIdentifier: !Ref DBClusterIdentifier Engine: aurora-mysql MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword DBSubnetGroupName: !Ref DBSubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup DatabaseName: mydb EngineMode: provisioned Port: 3306 DBInstanceWriter: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: !Sub ${DBClusterIdentifier}-writer DBClusterIdentifier: !Ref DBCluster Engine: aurora-mysql DBInstanceClass: db.t3.medium DBInstanceReader: Type: AWS::RDS::DBInstance DependsOn: DBInstanceWriter Properties: DBInstanceIdentifier: !Sub ${DBClusterIdentifier}-reader DBClusterIdentifier: !Ref DBCluster Engine: aurora-mysql DBInstanceClass: db.t3.medium PromotionTier: 2 Outputs: ClusterEndpoint: Description: Writer endpoint Value: !GetAtt DBCluster.Endpoint ReaderEndpoint: Description: Reader endpoint Value: !GetAtt DBCluster.ReadEndpoint
Template Structure
Template Sections Overview
AWS CloudFormation templates are JSON or YAML files with specific sections. Each section serves a purpose in defining your infrastructure.
AWSTemplateFormatVersion: 2010-09-09 # Required - template version Description: Optional description string # Optional description # Section order matters for readability but CloudFormation accepts any order Mappings: {} # Static configuration tables Metadata: {} # Additional information about resources Parameters: {} # Input values for customization Rules: {} # Parameter validation rules Conditions: {} # Conditional resource creation Transform: {} # Macro processing (e.g., AWS::Serverless) Resources: {} # AWS resources to create (REQUIRED) Outputs: {} # Return values after stack creation
Format Version
The
AWSTemplateFormatVersion identifies the template version. Current version is 2010-09-09.
AWSTemplateFormatVersion: 2010-09-09 Description: My RDS Database Template
Description
Add a description to document the template's purpose. Must appear after the format version.
AWSTemplateFormatVersion: 2010-09-09 Description: > This template creates an RDS MySQL instance with: - Multi-AZ deployment for high availability - Encrypted storage - Automated backups - Performance Insights enabled
Metadata
Use
Metadata for additional information about resources or parameters, including AWS::CloudFormation::Interface for parameter grouping.
Metadata: AWS::CloudFormation::Interface: ParameterGroups: - Label: default: Database Configuration Parameters: - DBInstanceIdentifier - Engine - DBInstanceClass - Label: default: Credentials Parameters: - MasterUsername - MasterUserPassword - Label: default: Network Parameters: - DBSubnetGroupName - VPCSecurityGroups ParameterLabels: DBInstanceIdentifier: default: Database Instance ID MasterUsername: default: Master Username
Resources Section
The
Resources section is the only required section. It defines AWS resources to provision.
Resources: # DB Subnet Group (required for VPC deployment) DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnet group for RDS deployment SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 # DB Parameter Group DBParameterGroup: Type: AWS::RDS::DBParameterGroup Properties: Description: Custom parameter group for MySQL Family: mysql8.0 Parameters: max_connections: 200 innodb_buffer_pool_size: 1073741824 # DB Instance DBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: mydbinstance DBInstanceClass: db.t3.micro Engine: mysql MasterUsername: admin MasterUserPassword: !Ref DBPassword DBSubnetGroupName: !Ref DBSubnetGroup DBParameterGroupName: !Ref DBParameterGroup
Parameters
Parameter Types
Use AWS-specific parameter types for validation and easier selection in the console.
Parameters: # DB instance identifier DBInstanceIdentifier: Type: String Description: Database instance identifier # AWS-specific parameter types for validation DBInstanceClass: Type: AWS::RDS::DBInstance::InstanceType Description: RDS instance class Default: db.t3.micro # Engine version from SSM EngineVersion: Type: AWS::RDS::DBInstance::Version Description: Database engine version Default: 8.0 # For existing VPC security groups VPCSecurityGroups: Type: List<AWS::EC2::SecurityGroup::Id> Description: Security groups for RDS instance
AWS::RDS::DBInstance::InstanceType Values
Common RDS instance types:
Parameters: DBInstanceClass: Type: String AllowedValues: - db.t3.micro - db.t3.small - db.t3.medium - db.t3.large - db.t3.xlarge - db.t3.2xlarge - db.m5.large - db.m5.xlarge - db.m5.2xlarge - db.m5.4xlarge - db.r5.large - db.r5.xlarge - db.r5.2xlarge
Parameter Constraints
Add constraints to validate parameter values.
Parameters: DBInstanceIdentifier: Type: String Description: Database instance identifier Default: mydatabase AllowedPattern: "^[a-zA-Z][a-zA-Z0-9]*$" ConstraintDescription: Must begin with a letter; contain only alphanumeric characters MinLength: 1 MaxLength: 63 MasterUsername: Type: String Description: Master username Default: admin AllowedPattern: "^[a-zA-Z][a-zA-Z0-9]*$" MinLength: 1 MaxLength: 16 NoEcho: true MasterUserPassword: Type: String Description: Master user password NoEcho: true MinLength: 8 MaxLength: 41 AllowedPattern: "[a-zA-Z0-9]*" AllocatedStorage: Type: Number Description: Allocated storage in GB Default: 20 MinValue: 20 MaxValue: 65536 DBPort: Type: Number Description: Database port Default: 3306 MinValue: 1150 MaxValue: 65535
Engine and Version Parameters
Parameters: Engine: Type: String Description: Database engine Default: mysql AllowedValues: - mysql - postgres - oracle-ee - oracle-se2 - sqlserver-ee - sqlserver-se - sqlserver-ex - sqlserver-web - aurora - aurora-mysql - aurora-postgresql - mariadb EngineVersion: Type: String Description: Database engine version Default: 8.0.35 DBFamily: Type: String Description: Parameter group family Default: mysql8.0 AllowedValues: - mysql5.6 - mysql5.7 - mysql8.0 - postgres11 - postgres12 - postgres13 - postgres14 - postgres15 - postgres16 - aurora5.6 - aurora-mysql5.7 - aurora-mysql8.0 - aurora-postgresql11 - aurora-postgresql14
SSM Parameter Types
Reference Systems Manager parameters for dynamic values.
Parameters: LatestMySQLVersion: Type: AWS::SSM::Parameter::Value<String> Description: Latest MySQL version from SSM Default: /rds/mysql/latest/version LatestPostgreSQLVersion: Type: AWS::SSM::Parameter::Value<String> Description: Latest PostgreSQL version from SSM Default: /rds/postgres/latest/version
NoEcho for Sensitive Data
Use
NoEcho for passwords and sensitive values to mask them in console output.
Parameters: MasterUserPassword: Type: String Description: Master user password NoEcho: true MinLength: 8 MaxLength: 41
Mappings
Use
Mappings for static configuration data based on regions or instance types.
Mappings: InstanceTypeConfig: db.t3.micro: CPU: 2 MemoryGiB: 1 StorageGB: 20 db.t3.small: CPU: 2 MemoryGiB: 2 StorageGB: 20 db.t3.medium: CPU: 2 MemoryGiB: 4 StorageGB: 20 db.m5.large: CPU: 2 MemoryGiB: 8 StorageGB: 100 RegionDatabasePort: us-east-1: MySQL: 3306 PostgreSQL: 5432 us-west-2: MySQL: 3306 PostgreSQL: 5432 eu-west-1: MySQL: 3306 PostgreSQL: 5432 Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceClass: !FindInMap [InstanceTypeConfig, !Ref DBInstanceClass, CPU] Engine: mysql # ...
Conditions
Use
Conditions to conditionally create resources based on parameters.
Parameters: EnableMultiAZ: Type: String Default: false AllowedValues: - true - false EnableEncryption: Type: String Default: true AllowedValues: - true - false Environment: Type: String Default: development AllowedValues: - development - staging - production Conditions: IsMultiAZ: !Equals [!Ref EnableMultiAZ, true] IsEncrypted: !Equals [!Ref EnableEncryption, true] IsProduction: !Equals [!Ref Environment, production] Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: MultiAZ: !Ref EnableMultiAZ StorageEncrypted: !Ref EnableEncryption # Production gets automated backups BackupRetentionPeriod: !If [IsProduction, 35, 7] DeletionProtection: !If [IsProduction, true, false]
Condition Functions
Conditions: IsDev: !Equals [!Ref Environment, development] IsStaging: !Equals [!Ref Environment, staging] IsProduction: !Equals [!Ref Environment, production] HasLicense: !Not [!Condition IsDev] Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: # Use license-included for production LicenseModel: !If [HasLicense, "license-included", "bring-your-own-license"] # Production uses provisioned IOPS StorageType: !If [IsProduction, "io1", "gp3"] Iops: !If [IsProduction, 3000, !Ref AWS::NoValue]
Transform
Use
Transform for macros like AWS::Serverless for SAM templates.
AWSTemplateFormatVersion: 2010-09-09 Transform: AWS::Serverless-2016-10-31 Description: Serverless RDS application template Globals: Function: Timeout: 30 Runtime: python3.11 Resources: RDSFunction: Type: AWS::Serverless::Function Properties: Handler: app.handler CodeUri: function/ Policies: - RDSFullAccessPolicy: DBInstanceIdentifier: !Ref DBInstanceIdentifier Environment: Variables: DB_HOST: !GetAtt DBInstance.Endpoint.Address DB_NAME: !Ref DBName DB_USER: !Ref MasterUsername
Outputs and Cross-Stack References
Basic Outputs
Outputs: DBInstanceId: Description: Database Instance ID Value: !Ref DBInstance DBInstanceEndpoint: Description: Database endpoint address Value: !GetAtt DBInstance.Endpoint.Address DBInstancePort: Description: Database port Value: !GetAtt DBInstance.Endpoint.Port DBInstanceArn: Description: Database Instance ARN Value: !GetAtt DBInstance.Arn DBInstanceClass: Description: Database Instance Class Value: !Ref DBInstanceClass
Exporting Values for Cross-Stack References
Export values so other stacks can import them.
Outputs: DBInstanceId: Description: Database Instance ID for other stacks Value: !Ref DBInstance Export: Name: !Sub ${AWS::StackName}-DBInstanceId DBInstanceEndpoint: Description: Database endpoint for application stacks Value: !GetAtt DBInstance.Endpoint.Address Export: Name: !Sub ${AWS::StackName}-DBEndpoint DBInstancePort: Description: Database port for application stacks Value: !GetAtt DBInstance.Endpoint.Port Export: Name: !Sub ${AWS::StackName}-DBPort DBConnectionString: Description: Full connection string for applications Value: !Sub jdbc:mysql://${DBInstanceEndpoint}:${DBInstancePort}/${DBName} Export: Name: !Sub ${AWS::StackName}-DBConnectionString
Importing Values in Another Stack
Parameters: # Import via AWS::RDS::DBInstance::Id for console selection DBInstanceId: Type: AWS::RDS::DBInstance::Id Description: RDS instance ID from database stack # Or use Fn::ImportValue for programmatic access DBEndpoint: Type: String Description: Database endpoint address Resources: ApplicationDatabaseConfig: Type: AWS::SSM::Parameter Properties: Name: /app/database/endpoint Value: !Ref DBEndpoint Type: String
Cross-Stack Reference Pattern
Create a dedicated database stack that exports values:
# database-stack.yaml AWSTemplateFormatVersion: 2010-09-09 Description: Database infrastructure stack Parameters: EnvironmentName: Type: String Default: production Resources: DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: !Sub Subnet group for ${EnvironmentName} SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 DBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceClass.t3.medium: db Engine: mysql MasterUsername: admin MasterUserPassword: !Ref DBPassword DBSubnetGroupName: !Ref DBSubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup MultiAZ: true StorageEncrypted: true Outputs: DBInstanceId: Value: !Ref DBInstance Export: Name: !Sub ${EnvironmentName}-DBInstanceId DBEndpoint: Value: !GetAtt DBInstance.Endpoint.Address Export: Name: !Sub ${EnvironmentName}-DBEndpoint DBArn: Value: !GetAtt DBInstance.Arn Export: Name: !Sub ${EnvironmentName}-DBArn DBSubnetGroupName: Value: !Ref DBSubnetGroup Export: Name: !Sub ${EnvironmentName}-DBSubnetGroupName
Application stack imports these values:
# application-stack.yaml AWSTemplateFormatVersion: 2010-09-09 Description: Application stack that imports from database stack Parameters: DatabaseStackName: Type: String Description: Name of the database stack Default: database-stack Resources: ApplicationConfig: Type: AWS::SSM::Parameter Properties: Name: /app/database/endpoint Value: !ImportValue Fn::Sub: ${DatabaseStackName}-DBEndpoint Type: String LambdaFunction: Type: AWS::Lambda::Function Properties: Runtime: python3.11 Handler: app.handler Environment: Variables: DB_ENDPOINT: !ImportValue Fn::Sub: ${DatabaseStackName}-DBEndpoint
RDS Database Components
DB Subnet Group
Required for VPC deployment. Must include at least 2 subnets in different AZs.
Resources: DBSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupDescription: Subnet group for RDS instance SubnetIds: - !Ref PrivateSubnet1 - !Ref PrivateSubnet2 - !Ref PrivateSubnet3 Tags: - Key: Name Value: !Sub ${AWS::StackName}-dbsubnet
DB Parameter Group
Custom parameter groups for database configuration.
Resources: DBParameterGroup: Type: AWS::RDS::DBParameterGroup Properties: Description: Custom parameter group for MySQL 8.0 Family: mysql8.0 Parameters: # Connection settings max_connections: 200 max_user_connections: 200 # Memory settings innodb_buffer_pool_size: 1073741824 innodb_buffer_pool_instances: 4 # Query cache (MySQL 5.7) query_cache_type: 1 query_cache_size: 268435456 # Timezone default_time_zone: "+00:00" # Character set character_set_server: utf8mb4 collation_server: utf8mb4_unicode_ci Tags: - Key: Name Value: !Sub ${AWS::StackName}-dbparam
DB Option Group
For database features like Oracle XML or SQL Server features.
Resources: DBOptionGroup: Type: AWS::RDS::DBOptionGroup Properties: EngineName: oracle-ee MajorEngineVersion: "19" OptionGroupDescription: Option group for Oracle 19c Options: - OptionName: OEM OptionVersion: "19" Port: 5500 VpcSecurityGroupMemberships: - !Ref OEMSecurityGroup - OptionName: SSL OptionSettings: - Name: SQLNET.SSL_VERSION Value: "1.2"
DB Instance - MySQL
Resources: MySQLDBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: mysql-instance DBInstanceClass: db.t3.medium Engine: mysql EngineVersion: "8.0.35" MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword AllocatedStorage: "100" StorageType: gp3 DBSubnetGroupName: !Ref DBSubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup DBParameterGroupName: !Ref DBParameterGroup StorageEncrypted: true MultiAZ: true BackupRetentionPeriod: 35 DeletionProtection: true EnablePerformanceInsights: true PerformanceInsightsRetentionPeriod: 731 AutoMinorVersionUpgrade: false Tags: - Key: Environment Value: !Ref Environment
DB Instance - PostgreSQL
Resources: PostgreSQLDBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: postgres-instance DBInstanceClass: db.t3.medium Engine: postgres EngineVersion: "16.1" MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword AllocatedStorage: "100" StorageType: gp3 DBSubnetGroupName: !Ref DBSubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup DBParameterGroupName: !Ref DBParameterGroup StorageEncrypted: true MultiAZ: true BackupRetentionPeriod: 35 DeletionProtection: true EnablePerformanceInsights: true PubliclyAccessible: false
Aurora MySQL Cluster
Resources: AuroraMySQLCluster: Type: AWS::RDS::DBCluster Properties: DBClusterIdentifier: aurora-mysql-cluster Engine: aurora-mysql EngineVersion: "8.0.mysql_aurora.3.02.0" MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword DatabaseName: mydb DBSubnetGroupName: !Ref DBSubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup DBClusterParameterGroupName: !Ref AuroraClusterParameterGroup StorageEncrypted: true EngineMode: provisioned Port: 3306 EnableIAMDatabaseAuthentication: true AuroraDBInstanceWriter: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: aurora-writer DBClusterIdentifier: !Ref AuroraMySQLCluster Engine: aurora-mysql DBInstanceClass: db.r5.large PromotionTier: 1 AuroraDBInstanceReader: Type: AWS::RDS::DBInstance DependsOn: AuroraDBInstanceWriter Properties: DBInstanceIdentifier: aurora-reader DBClusterIdentifier: !Ref AuroraMySQLCluster Engine: aurora-mysql DBInstanceClass: db.r5.large PromotionTier: 2
Aurora PostgreSQL Cluster
Resources: AuroraPostgresCluster: Type: AWS::RDS::DBCluster Properties: DBClusterIdentifier: aurora-pg-cluster Engine: aurora-postgresql EngineVersion: "15.4" MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword DatabaseName: mydb DBSubnetGroupName: !Ref DBSubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup StorageEncrypted: true EngineMode: provisioned Port: 5432 AuroraPostgresInstanceWriter: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: aurora-pg-writer DBClusterIdentifier: !Ref AuroraPostgresCluster Engine: aurora-postgresql DBInstanceClass: db.r5.large PromotionTier: 1 AuroraPostgresInstanceReader: Type: AWS::RDS::DBInstance DependsOn: AuroraPostgresInstanceWriter Properties: DBInstanceIdentifier: aurora-pg-reader DBClusterIdentifier: !Ref AuroraPostgresCluster Engine: aurora-postgresql DBInstanceClass: db.r5.large PromotionTier: 2
Aurora Serverless Cluster
Resources: AuroraServerlessCluster: Type: AWS::RDS::DBCluster Properties: DBClusterIdentifier: aurora-serverless Engine: aurora-mysql EngineVersion: "5.6.mysql_aurora.2.12.0" MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword DatabaseName: mydb DBSubnetGroupName: !Ref DBSubnetGroup VPCSecurityGroups: - !Ref DBSecurityGroup EngineMode: serverless ScalingConfiguration: AutoPause: true MinCapacity: 2 MaxCapacity: 32 SecondsUntilAutoPause: 300
DB Cluster Parameter Group (Aurora)
Resources: AuroraClusterParameterGroup: Type: AWS::RDS::DBClusterParameterGroup Properties: Description: Custom cluster parameter group for Aurora MySQL Family: aurora-mysql8.0 Parameters: character_set_server: utf8mb4 collation_server: utf8mb4_unicode_ci max_connections: 1000 innodb_buffer_pool_size: 2147483648 slow_query_log: "ON" long_query_time: 2
Security and Secrets
Using Secrets Manager for Credentials
Resources: DBCredentialsSecret: Type: AWS::SecretsManager::Secret Properties: Name: !Sub ${AWS::StackName}/rds/credentials Description: RDS database credentials SecretString: !Sub | { "username": "${MasterUsername}", "password": "${MasterUserPassword}", "host": !GetAtt DBInstance.Endpoint.Address, "port": !GetAtt DBInstance.Endpoint.Port, "dbname": "mydb" } DBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceClass: db.t3.medium Engine: mysql MasterUsername: !Sub "{{resolve:secretsmanager:${DBCredentialsSecret}:SecretString:username}}" MasterUserPassword: !Sub "{{resolve:secretsmanager:${DBCredentialsSecret}:SecretString:password}}" # ...
DB Security Group (for EC2-Classic)
Resources: DBSecurityGroup: Type: AWS::RDS::DBSecurityGroup Properties: DBSecurityGroupDescription: Security group for RDS instance EC2VpcId: !Ref VPCId # For EC2-Classic, use DBSecurityGroupIngress DBSecurityGroupIngress: - EC2SecurityGroupId: !Ref AppSecurityGroup - EC2SecurityGroupName: default
VPC Security Groups (Recommended)
For VPC deployment, use EC2 security groups instead:
Resources: DBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security group for RDS VpcId: !Ref VPCId GroupName: !Sub ${AWS::StackName}-rds-sg SecurityGroupIngress: - IpProtocol: tcp FromPort: 3306 ToPort: 3306 SourceSecurityGroupId: !Ref AppSecurityGroup Tags: - Key: Name Value: !Sub ${AWS::StackName}-rds-sg AppSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupDescription: Security group for application VpcId: !Ref VPCId SecurityGroupEgress: - IpProtocol: tcp FromPort: 3306 ToPort: 3306 DestinationSecurityGroupId: !Ref DBSecurityGroup
High Availability and Multi-AZ
Multi-AZ Deployment
Parameters: EnableMultiAZ: Type: String Default: true AllowedValues: - true - false Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: # Multi-AZ is not supported for Aurora clusters (automatic) MultiAZ: !Ref EnableMultiAZ # For multi-AZ, use a standby in a different AZ AvailabilityZone: !If - IsMultiAZ - !Select [1, !GetAZs ''] - !Ref AWS::NoValue # For single-AZ, specify no AZ (AWS selects)
Read Replicas
Resources: # Primary instance PrimaryDBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceClass: db.r5.large Engine: mysql SourceDBInstanceIdentifier: !Ref ExistingDBInstance # Read replica in different region CrossRegionReadReplica: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: my-cross-region-replica SourceDBInstanceIdentifier: !Sub arn:aws:rds:us-west-2:${AWS::AccountId}:db:${PrimaryDBInstance} DBInstanceClass: db.r5.large Engine: mysql
Enhanced Monitoring and Performance Insights
Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: EnablePerformanceInsights: true PerformanceInsightsRetentionPeriod: 731 PerformanceInsightsKMSKeyId: !Ref PerformanceInsightsKey # Enhanced Monitoring MonitoringInterval: 60 MonitoringRoleArn: !GetAtt MonitoringRole.Arn # Database insights EnableCloudwatchLogsExports: - audit - error - general - slowquery # IAM Role for Enhanced Monitoring Resources: MonitoringRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: monitoring.rds.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonRDSEnhancedMonitoringRole
Best Practices
Use AWS-Specific Parameter Types
Always use AWS-specific parameter types for validation and easier selection.
Parameters: DBInstanceClass: Type: AWS::RDS::DBInstance::InstanceType Description: RDS instance type DBInstanceIdentifier: Type: String AllowedPattern: "^[a-zA-Z][a-zA-Z0-9]*$"
Enable Encryption at Rest
Always enable encryption for production databases.
Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: StorageEncrypted: true KmsKeyId: !Ref EncryptionKey
Use Multi-AZ for Production
Conditions: IsProduction: !Equals [!Ref Environment, production] Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: MultiAZ: !If [IsProduction, true, false] BackupRetentionPeriod: !If [IsProduction, 35, 7] DeletionProtection: !If [IsProduction, true, false]
Enable Performance Insights
Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: EnablePerformanceInsights: true PerformanceInsightsRetentionPeriod: 731 PerformanceInsightsKMSKeyId: !Ref PK
Use Proper Naming Conventions
Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: Tags: - Key: Name Value: !Sub ${Environment}-${Application}-rds - Key: Environment Value: !Ref Environment - Key: Application Value: !Ref ApplicationName - Key: ManagedBy Value: CloudFormation
Use Secrets Manager for Credentials
Resources: DBCredentials: Type: AWS::SecretsManager::Secret Properties: Name: !Sub ${AWS::StackName}/rds/credentials SecretString: !Sub '{"username":"${MasterUsername}","password":"${MasterUserPassword}"}' DBInstance: Type: AWS::RDS::DBInstance Properties: MasterUsername: !Sub "{{resolve:secretsmanager:${DBCredentials}:SecretString:username}}" MasterUserPassword: !Sub "{{resolve:secretsmanager:${DBCredentials}:SecretString:password}}"
Separate Database and Application Stacks
# database-stack.yaml - Rarely changes AWSTemplateFormatVersion: 2010-09-09 Description: Database infrastructure (VPC, subnets, RDS instance) Resources: DBSubnetGroup: AWS::RDS::DBSubnetGroup DBInstance: AWS::RDS::DBInstance DBParameterGroup: AWS::RDS::DBParameterGroup # application-stack.yaml - Changes frequently AWSTemplateFormatVersion: 2010-09-09 Description: Application resources Parameters: DatabaseStackName: Type: String Resources: ApplicationConfig: AWS::SSM::Parameter
Use Pseudo Parameters
Use pseudo parameters for region-agnostic templates.
Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: !Sub ${AWS::StackName}-${AWS::Region} Tags: - Key: Region Value: !Ref AWS::Region - Key: AccountId Value: !Ref AWS::AccountId
Validate Before Deployment
# Validate template aws cloudformation validate-template --template-body file://template.yaml # Use cfn-lint for advanced validation pip install cfn-lint cfn-lint template.yaml # Check for AWS-specific issues cfn-lint template.yaml --region us-east-1
Stack Policies
Stack policies protect critical resources from unintended updates during stack operations. For RDS databases, this is essential to prevent accidental modifications that could cause data loss or downtime.
Basic Stack Policy
{ "Statement" : [ { "Effect" : "Allow", "Action" : "Update:*", "Principal": "*", "Resource" : "*" }, { "Effect" : "Deny", "Action" : "Update:*", "Principal": "*", "Resource" : "LogicalResourceId/DBInstance" }, { "Effect" : "Deny", "Action" : "Update:*", "Principal": "*", "Resource" : "LogicalResourceId/DBCluster" } ] }
Stack Policy for Production RDS
{ "Statement": [ { "Effect": "Allow", "Action": "Update:*", "Principal": "*", "Resource": "*" }, { "Effect": "Deny", "Action": [ "Update:Replace", "Update:Delete" ], "Principal": "*", "Resource": "LogicalResourceId/DBInstance" }, { "Effect": "Deny", "Action": [ "Update:Replace", "Update:Delete" ], "Principal": "*", "Resource": "LogicalResourceId/DBCluster" }, { "Effect": "Deny", "Action": "Update:Delete", "Principal": "*", "Resource": "LogicalResourceId/DBSubnetGroup" }, { "Effect": "Allow", "Action": "Update:Modify", "Principal": "*", "Resource": "LogicalResourceId/DBInstance", "Condition": { "StringEquals": { "ResourceAttribute/StorageEncrypted": "true" } } } ] }
Setting Stack Policy
# Set stack policy during creation aws cloudformation create-stack \ --stack-name my-rds-stack \ --template-body file://template.yaml \ --stack-policy-body file://stack-policy.json # Set stack policy on existing stack aws cloudformation set-stack-policy \ --stack-name my-rds-stack \ --stack-policy-body file://stack-policy.json # View current stack policy aws cloudformation get-stack-policy \ --stack-name my-rds-stack \ --query StackPolicyBody \ --output text
Termination Protection
Termination protection is critical for RDS databases as it prevents accidental deletion that could result in data loss. This should be enabled for all production databases.
Enabling Termination Protection
# Enable termination protection on stack creation aws cloudformation create-stack \ --stack-name production-rds \ --template-body file://template.yaml \ --enable-termination-protection # Enable termination protection on existing stack aws cloudformation update-termination-protection \ --stack-name production-rds \ --enable-termination-protection # Check if termination protection is enabled aws cloudformation describe-stacks \ --stack-name production-rds \ --query 'Stacks[0].EnableTerminationProtection' \ --output boolean # Disable termination protection (requires confirmation) aws cloudformation update-termination-protection \ --stack-name production-rds \ --no-enable-termination-protection
Termination Protection in Template
AWSTemplateFormatVersion: 2010-09-09 Description: RDS instance with termination protection enabled Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: DBInstanceIdentifier: production-db DBInstanceClass: db.r5.large Engine: mysql MasterUsername: !Ref MasterUsername MasterUserPassword: !Ref MasterUserPassword StorageEncrypted: true MultiAZ: true DeletionProtection: true # Termination protection is set at stack level, not resource level
Deletion Protection vs Termination Protection
| Feature | DeletionProtection | Termination Protection |
|---|---|---|
| Level | Resource level (DBInstance) | Stack level |
| Prevents | DELETE_DB_INSTANCE API call | CloudFormation stack deletion |
| Console UI | Instance settings | Stack settings |
| Override | Cannot be overridden | Can be disabled with confirmation |
| Recommended for | All production RDS instances | All production stacks with RDS |
Deletion Protection Best Practice
Conditions: IsProduction: !Equals [!Ref Environment, production] Resources: DBInstance: Type: AWS::RDS::DBInstance Properties: # Always enable deletion protection DeletionProtection: !If [IsProduction, true, false] # Additional production safeguards MultiAZ: !If [IsProduction, true, false] BackupRetentionPeriod: !If [IsProduction, 35, 7]
Drift Detection
Drift detection identifies when the actual infrastructure configuration differs from the CloudFormation template. This is crucial for RDS to ensure security and compliance.
Detecting Drift
# Detect drift on entire stack aws cloudformation detect-stack-drift \ --stack-name production-rds # Detect drift on specific resources aws cloudformation detect-stack-drift \ --stack-name production-rds \ --logical-resource-ids DBInstance,DBParameterGroup # Get drift detection status aws cloudformation describe-stack-drift-detection-status \ --stack-drift-detection-id <detection-id> # Check drift status for all resources aws cloudformation describe-stack-resource-drifts \ --stack-name production-rds
Drift Detection Status Response
{ "StackResourceDrifts": [ { "LogicalResourceId": "DBInstance", "PhysicalResourceId": "production-db-instance-id", "ResourceType": "AWS::RDS::DBInstance", "StackId": "arn:aws:cloudformation:us-east-1:123456789:stack/production-rds/...", "DriftStatus": "MODIFIED", "PropertyDifferences": [ { "PropertyPath": "MultiAZ", "ExpectedValue": "true", "ActualValue": "false" }, { "PropertyPath": "BackupRetentionPeriod", "ExpectedValue": "35", "ActualValue": "7" } ] } ] }
Automated Drift Detection Schedule
# Create a Lambda function to check drift weekly # and send SNS notification if drift is detected aws events put-rule \ --name rds-drift-detection \ --schedule-expression "rate(7 days)" aws events put-targets \ --rule rds-drift-detection \ --targets "Id"="1","Arn"="arn:aws:lambda:us-east-1:123456789:function/drift-checker"
Drift Detection Script
#!/bin/bash # check-rds-drift.sh STACK_NAME=$1 DRIFT_STATUS=$(aws cloudformation detect-stack-drift \ --stack-name $STACK_NAME \ --query StackDriftStatus \ --output text 2>/dev/null) if [ "$DRIFT_STATUS" == "DRIFTED" ]; then echo "Drift detected on stack $STACK_NAME" aws cloudformation describe-stack-resources \ --stack-name $STACK_NAME \ --query 'StackResources[?ResourceStatusReason!=`null`]' \ --output table # Send notification aws sns publish \ --topic-arn arn:aws:sns:us-east-1:123456789:rds-drift-alert \ --message "Drift detected on stack $STACK_NAME" else echo "No drift detected on stack $STACK_NAME" fi
Change Sets
Change sets allow you to preview how proposed changes will affect your stack before execution. This is essential for RDS to understand potential impact.
Creating and Viewing a Change Set
# Create change set for stack update aws cloudformation create-change-set \ --stack-name production-rds \ --change-set-name preview-changes \ --template-body file://updated-template.yaml \ --capabilities CAPABILITY_IAM \ --change-set-type UPDATE # List change sets for a stack aws cloudformation list-change-sets \ --stack-name production-rds # Describe change set aws cloudformation describe-change-set \ --stack-name production-rds \ --change-set-name preview-changes # Execute change set aws cloudformation execute-change-set \ --stack-name production-rds \ --change-set-name preview-changes # Delete change set (if not executing) aws cloudformation delete-change-set \ --stack-name production-rds \ --change-set-name preview-changes
Change Set Response Example
{ "ChangeSetName": "preview-changes", "ChangeSetId": "arn:aws:cloudformation:us-east-1:123456789:changeSet/...", "StackId": "arn:aws:cloudformation:us-east-1:123456789:stack/...", "Status": "CREATE_COMPLETE", "Changes": [ { "Type": "Resource", "ResourceChange": { "Action": "Modify", "LogicalResourceId": "DBInstance", "PhysicalResourceId": "production-db", "ResourceType": "AWS::RDS::DBInstance", "Replacement": "False", "Scope": [ "Properties" ], "Details": [ { "Target": { "Attribute": "Properties", "Name": "MultiAZ" }, "Evaluation": "Static", "ChangeSource": "Parameter", "BeforeValue": "false", "AfterValue": "true" } ] } } ] }
Change Set for RDS Modifications
# Change set that will modify RDS instance class aws cloudformation create-change-set \ --stack-name production-rds \ --change-set-name modify-instance-class \ --template-body file://modify-instance-template.yaml \ --parameters parameter-overrides DBInstanceClass=db.r5.xlarge # Change set for adding read replica aws cloudformation create-change-set \ --stack-name production-rds \ --change-set-name add-read-replica \ --template-body file://add-replica-template.yaml # Change set that requires replacement (causes downtime) aws cloudformation create-change-set \ --stack-name production-rds \ --change-set-name change-engine-version \ --template-body file://change-version-template.yaml
Change Set Types
| Change Set Type | Description | Use Case |
|---|---|---|
| Creates changes for existing stack | Modifying existing resources |
| Simulates stack creation | Validating new templates |
| Imports existing resources | Moving resources to CloudFormation |
Change Set Best Practices for RDS
# Always create change set before updating RDS aws cloudformation create-change-set \ --stack-name production-rds \ --change-set-name pre-update-preview \ --template-body file://updated-template.yaml # Review changes carefully aws cloudformation describe-change-set \ --stack-name production-rds \ --change-set-name pre-update-preview \ --query 'Changes[].ResourceChange' # Check for replacement operations aws cloudformation describe-change-set \ --stack-name production-rds \ --change-set-name pre-update-preview \ --query 'Changes[?ResourceChange.Replacement==`True`]' # Only execute if changes are acceptable aws cloudformation execute-change-set \ --stack-name production-rds \ --change-set-name pre-update-preview
Related Resources
- For advanced patterns: See EXAMPLES.md
- For reference: See REFERENCE.md
- AWS CloudFormation User Guide: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/
- RDS Documentation: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/
- RDS Best Practices: https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/CHAP_BestPractices.html
- Aurora Documentation: https://docs.aws.amazon.com/AmazonRDS/latest/AuroraUserGuide/