【AWS】CloudformationでRDSと

1. 使用するサービス

(AWS)

  • Cloudformation
  • Lambda
  • Subnet
  • Security Group
  • RDS
  • RDS Proxy

2.概要

仕事でCloudformationを触る機会が増えたので備忘録として残します。最近グローバルリリースされたRDS Proxyを検索しても中々これといったCloudformationで作成した記事が無いので色々、調べた結果作成されたテンプレートを載せます。

3. 実装

3-1. 実装イメージ

各種セキュリティグループでアクセス制御します。RDSのパスワード等はSystem Managerに保管します。

architecture_image
実装イメージ

3-2. テンプレート

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Outputs the time

Parameters:
    
  DBName:
    Description: "DB Name"
    Default: test
    Type: String

Resources:
          
  TestFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: python3.8
      CodeUri: ./test
      Role: !GetAtt TestRole.Arn
      VpcConfig:
        SecurityGroupIds:
          - !Ref TestLambdaSecurityGroup
        SubnetIds:
          - !Ref TestPubSubnet1a
          - !Ref TestPubSubnet1c
      Environment: 
        Variables:
          RDSEndpoint: !GetAtt RDSProxyTest.Endpoint
          MasterUsername: !Sub '{{resolve:secretsmanager:${RDSTestInstanceRotationSecret}:SecretString:username}}'
          MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSTestInstanceRotationSecret}:SecretString:password}}'
          DBName: !Ref DBName
              
  TestRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: "TestRole"
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          -
            Effect: "Allow"
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AmazonRDSFullAccess
        - arn:aws:iam::aws:policy/AmazonEC2FullAccess
        
      MaxSessionDuration: 3600
      Path: "/"
      
  TestVPC:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: 10.0.0.0/16
      Tags:
      - Key: "Name"
        Value: "TestVpc"
        
  TestPubSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: 
        Ref: TestVPC
      CidrBlock: 10.0.1.0/24
      AvailabilityZone: "ap-northeast-1a"
      Tags:
      - Key: "Name"
        Value: "TestPubSubnet1a"
  
  TestPubSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: 
        Ref: TestVPC
      CidrBlock: 10.0.2.0/24
      AvailabilityZone: "ap-northeast-1c"
      Tags:
      - Key: "Name"
        Value: "TestPubSubnet1c"
  
  TestPriSubnet1a:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: 
        Ref: TestVPC
      CidrBlock: 10.0.3.0/24
      AvailabilityZone: "ap-northeast-1a"
      Tags:
      - Key: "Name"
        Value: "TestPriSubnet1a"
        
  TestPriSubnet1c:
    Type: AWS::EC2::Subnet
    Properties:
      VpcId: 
        Ref: TestVPC
      CidrBlock: 10.0.4.0/24
      AvailabilityZone: "ap-northeast-1c"
      Tags:
      - Key: "Name"
        Value: "TestPriSubnet1c"
  
  TestLambdaSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Nothing
        VpcId: 
          Ref: TestVPC
        Tags: 
        - Key: "Name"
          Value: "TestLambdaSecurityGroup"
          
  TestRDSSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Allow mysql to client host
        VpcId: 
          Ref: TestVPC
        Tags: 
        - Key: "Name"
          Value: "TestRDSSecurityGroup"
          
  TestRDSSecurityIngress:
   Type: 'AWS::EC2::SecurityGroupIngress'
   Properties:
      GroupId: !Ref TestRDSSecurityGroup
      IpProtocol: tcp
      FromPort: 3306
      ToPort: 3306
      SourceSecurityGroupId: !GetAtt TestRDSProxySecurityGroup.GroupId
  
  
  TestRDSProxySecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: Allow mysql to client host
        VpcId: 
          Ref: TestVPC
        Tags: 
        - Key: "Name"
          Value: "TestRDSProxySecurityGroup"
          
  TestRDSProxySecurityIngress:
   Type: 'AWS::EC2::SecurityGroupIngress'
   Properties:
      GroupId: !Ref TestRDSProxySecurityGroup
      IpProtocol: tcp
      FromPort: 3306
      ToPort: 3306
      SourceSecurityGroupId: !GetAtt TestLambdaSecurityGroup.GroupId
      
  
  TestDBInstance:
    Type: 'AWS::RDS::DBInstance'
    Properties:
      AllocatedStorage: 20
      DBInstanceClass: db.t2.micro
      DBInstanceIdentifier: -rds-test
      DBName: !Ref DBName
      DBSubnetGroupName: !Ref TestDBSubnetGroup
      Engine: mysql
      EngineVersion: 5.7.22
      MasterUsername: !Sub '{{resolve:secretsmanager:${RDSTestInstanceRotationSecret}:SecretString:username}}'
      MasterUserPassword: !Sub '{{resolve:secretsmanager:${RDSTestInstanceRotationSecret}:SecretString:password}}'
      PubliclyAccessible: false
      VPCSecurityGroups: 
        - !GetAtt TestRDSSecurityGroup.GroupId
      
  TestDBSubnetGroup:
    Type: 'AWS::RDS::DBSubnetGroup'
    Properties:
      DBSubnetGroupDescription: TestDBSubnetGroup
      DBSubnetGroupName: TestDBSubnetGroup
      SubnetIds:
        - !Ref TestPriSubnet1a
        - !Ref TestPriSubnet1c
        
  RDSProxyTest:
    Type: 'AWS::RDS::DBProxy'
    Properties:
      Auth:
        - AuthScheme: SECRETS
          IAMAuth: DISABLED
          SecretArn: !Ref RDSTestInstanceRotationSecret
      EngineFamily: MYSQL
      DBProxyName: -proxy-test
      IdleClientTimeout: 120
      RequireTLS: false
      RoleArn: !GetAtt SecretsManagerRole.Arn
      VpcSecurityGroupIds: 
        - !Ref TestRDSProxySecurityGroup
      VpcSubnetIds: 
        - !Ref TestPriSubnet1a
        - !Ref TestPriSubnet1c
      
  RDSProxyTargetGroupTest:
    Type: 'AWS::RDS::DBProxyTargetGroup'
    Properties:
      DBProxyName: !Ref RDSProxyTest
      DBInstanceIdentifiers: 
        - !Ref TestDBInstance
      ConnectionPoolConfigurationInfo:
          ConnectionBorrowTimeout: 120
          MaxConnectionsPercent: 100
          MaxIdleConnectionsPercent: 100
      TargetGroupName: default
      
     
  RDSTestInstanceRotationSecret:
    Type: 'AWS::SecretsManager::Secret'
    Properties:
      Description: 'This is my rds instance secret'
      GenerateSecretString:
        SecretStringTemplate: '{"username": "master"}'
        GenerateStringKey: "password"
        PasswordLength: 16
        ExcludeCharacters: '"@/\'
              
  SecretRDSInstanceAttachment:
    Type: 'AWS::SecretsManager::SecretTargetAttachment'
    Properties:
      SecretId: !Ref RDSTestInstanceRotationSecret
      TargetId: !Ref TestDBInstance
      TargetType: 'AWS::RDS::DBInstance'
  
    
  SecretsManagerManagedPolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties:
      Description: "Get values from Secrets Manager"
      Path: /
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "secretsmanager:GetResourcePolicy"
              - "secretsmanager:GetSecretValue"
              - "secretsmanager:DescribeSecret"
              - "secretsmanager:ListSecretVersionIds"
            Resource: !Ref RDSTestInstanceRotationSecret
          
          - Effect: Allow
            Action:
              - "kms:Decrypt"
            Resource: !Sub "*"
            Condition:
              StringEquals:
                kms:ViaService: !Sub "secretsmanager.${AWS::Region}.amazonaws.com"
  
  SecretsManagerRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - "rds.amazonaws.com"
            Action:
              - "sts:AssumeRole"
      Description: "Use for RDS Proxy"
      ManagedPolicyArns:
        - !Ref SecretsManagerManagedPolicy
      Path: /

何も問題なければこのテンプレートで一から作成されるはずです。
10〜15分くらいデプロイに時間かかると思います。