Skillsaws-cloudformation-rds
A

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.

giuseppe-trisciuoglio
73 stars
1.5k downloads
Updated 1w ago

Readme

aws-cloudformation-rds follows the SKILL.md standard. Use the install command to add it to your agent stack.

---
name: aws-cloudformation-rds
description: 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.
category: aws
tags: [aws, cloudformation, rds, database, mysql, postgresql, aurora, mariadb, oracle, infrastructure, iaac]
version: 1.0.0
allowed-tools: Read, Write, Bash
---

# AWS 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

```yaml
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

```yaml
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.

```yaml
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`.

```yaml
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.

```yaml
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.

```yaml
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.

```yaml
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.

```yaml
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:

```yaml
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.

```yaml
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

```yaml
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.

```yaml
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.

```yaml
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.

```yaml
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.

```yaml
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

```yaml
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.

```yaml
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

```yaml
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.

```yaml
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

```yaml
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:

```yaml
# 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:

```yaml
# 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.

```yaml
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.

```yaml
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.

```yaml
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

```yaml
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

```yaml
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

```yaml
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

```yaml
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

```yaml
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)

```yaml
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

```yaml
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)

```yaml
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:

```yaml
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

```yaml
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

```yaml
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

```yaml
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.

```yaml
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.

```yaml
Resources:
  DBInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      StorageEncrypted: true
      KmsKeyId: !Ref EncryptionKey
```

### Use Multi-AZ for Production

```yaml
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

```yaml
Resources:
  DBInstance:
    Type: AWS::RDS::DBInstance
    Properties:
      EnablePerformanceInsights: true
      PerformanceInsightsRetentionPeriod: 731
      PerformanceInsightsKMSKeyId: !Ref PK
```

### Use Proper Naming Conventions

```yaml
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

```yaml
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

```yaml
# 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.

```yaml
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

```bash
# 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

```yaml
{
  "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

```yaml
{
  "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

```bash
# 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

```bash
# 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

```yaml
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

```yaml
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

```bash
# 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

```json
{
  "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

```bash
# 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

```bash
#!/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

```bash
# 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

```json
{
  "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

```bash
# 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 |
|----------------|-------------|----------|
| `UPDATE` | Creates changes for existing stack | Modifying existing resources |
| `CREATE` | Simulates stack creation | Validating new templates |
| `IMPORT` | Imports existing resources | Moving resources to CloudFormation |

### Change Set Best Practices for RDS

```bash
# 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](EXAMPLES.md)
- For reference: See [REFERENCE.md](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/

Install

Requires askill CLI v1.0+

Metadata

LicenseUnknown
Version-
Updated1w ago
Publishergiuseppe-trisciuoglio

Tags

apidatabaselintingobservabilitysecurity