💻
Building and hosting a WebApp
  • Getting started
  • Project setup
    • Requirements
    • Files organisation
    • Lerna
    • Linter
    • Prettier
    • GitHook
    • Testing
    • Conclusion
  • Backend
    • Files organisation
    • Environment config
    • Express API
    • Security
    • Database
    • GraphQL
    • User authentication
    • Conclusion
  • Frontend
    • Create React App
    • Files organisation
    • Styles
    • Apollo Hooks
    • Form management
    • User authentication
    • Writing tests
    • Types generation
    • Conclusion
  • DevOps
    • CI/CD
    • AWS
      • Managing secrets
      • Pricing
      • RDS
      • S3
      • Route53
      • CloudFront
      • Serverless
      • Security
      • CloudFormation
    • Conclusion
  • 🚧Stripe payment
  • 🚧File upload
Powered by GitBook
On this page
  • How to use CloudFormation
  • Creating the resources

Was this helpful?

  1. DevOps
  2. AWS

CloudFormation

PreviousSecurityNextConclusion

Last updated 5 years ago

Was this helpful?

Everything can be done in the UI on console.aws.amazon.com. But we want to automate as much as possible.

For this reason, we won't create any resources manually anymore.

We will use instead so we can keep the infrastructure configuration under version control.

Read more .

  • If something went wrong, CloudFormation will automatically rollback the changes.

  • You can delete all the resources created with one click.

  • Use to see if something was changed manually instead of using CloudFormation.

How to use CloudFormation

You can use yml or json files. For yml files, resources take the following format:

ResourceName:
    Type: service-provider::service-name::data-type-name
    Properties:
        ...
ServerlessRDSCluster:
    Type: AWS::RDS::DBCluster
    Properties:
        Engine: aurora
        EngineMode: serverless
        Port: 3306
        DatabaseName: testdb
        MasterUsername: myuser
        MasterUserPassword: secret
        ScalingConfiguration:
            AutoPause: False
            MinCapacity: 2
            MaxCapacity: 256

Creating the resources

We can manage CloudFormation resources from serverless.yml.

Here's the full file to create everything needed:

serverless.yml
service: daedalost

provider:
  region: eu-west-1
  stage: ${opt:stage, 'dev'}
  name: aws
  runtime: nodejs10.x
  memorySize: 1024
  timeout: 30
  vpc:
    securityGroupIds:
      - Fn::GetAtt: [VPCStaticIP, DefaultSecurityGroup]
    subnetIds:
      - Ref: SubnetPrivate
      - Ref: SubnetPrivate2
  iamRoleStatements:
    - Effect: Allow
      Action:
        - ec2:CreateNetworkInterface
        - ec2:DeleteNetworkInterface
        - ec2:DescribeNetworkInterfaces
      Resource: "*"
  environment:
    SERVERLESS_EXPRESS_PLATFORM: aws
    NODE_ENV: production
    STAGE: ${opt:stage, 'development'}
    POSTGRES_DATABASE: ${self:service}
    POSTGRES_HOST:
      Fn::GetAtt:
        - ServerlessRDSCluster
        - Endpoint.Address
    POSTGRES_USERNAME: ${ssm:/${self:service}/${self:provider.stage}/POSTGRES_USERNAME}
    POSTGRES_PASSWORD: ${ssm:/${self:service}/${self:provider.stage}/POSTGRES_PASSWORD}
    JWT_SECRET: ${ssm:/${self:service}/${self:provider.stage}/JWT_SECRET}

plugins:
  - serverless-plugin-typescript
  - serverless-offline
  - serverless-finch

custom:
  client:
    bucketName: daedalost
    distributionFolder: ../web/build
    errorDocument: index.html

functions:
  server:
    handler: server.handler
    events:
      - http:
          path: /{proxy+}
          method: any
          cors: true

resources:
  Resources:
    # Step 1: Create a new VPC
    VPCStaticIP:
      Type: AWS::EC2::VPC
      Properties:
        CidrBlock: 11.0.0.0/16
        Tags:
          - Key: Name
            Value: ${self:service}-${self:provider.stage}-vpc

    # Step 2: Create 2 Subnets
    SubnetPublic:
      Type: AWS::EC2::Subnet
      Properties:
        AvailabilityZone: ${self:provider.region}a
        CidrBlock: 11.0.0.0/24
        Tags:
          - Key: Name
            Value: ${self:service}-${self:provider.stage}-public-subnet
        VpcId:
          Ref: VPCStaticIP

    SubnetPrivate:
      Type: AWS::EC2::Subnet
      Properties:
        AvailabilityZone: ${self:provider.region}b
        CidrBlock: 11.0.1.0/24
        Tags:
          - Key: Name
            Value: ${self:service}-${self:provider.stage}-private-subnet-b
        VpcId:
          Ref: VPCStaticIP

    SubnetPrivate2:
      Type: AWS::EC2::Subnet
      Properties:
        AvailabilityZone: ${self:provider.region}c
        CidrBlock: 11.0.2.0/24
        Tags:
          - Key: Name
            Value: ${self:service}-${self:provider.stage}-private-subnet-c
        VpcId:
          Ref: VPCStaticIP

    # Step 3: Create an Internet Gateway
    InternetGateway:
      Type: AWS::EC2::InternetGateway
      Properties:
        Tags:
          - Key: Name
            Value: ${self:service}-${self:provider.stage}-igw

    # Attach Internet Gateway to VPC
    VPCGatewayAttachment:
      Type: AWS::EC2::VPCGatewayAttachment
      Properties:
        InternetGatewayId:
          Ref: InternetGateway
        VpcId:
          Ref: VPCStaticIP

    # Step 4: Create a public Route Table and Assign it to our public route
    RouteTablePublic:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId:
          Ref: VPCStaticIP
        Tags:
          - Key: Name
            Value: ${self:service}-${self:provider.stage}-public-route

    RoutePublic:
      Type: AWS::EC2::Route
      Properties:
        DestinationCidrBlock: 0.0.0.0/0
        GatewayId:
          Ref: InternetGateway
        RouteTableId:
          Ref: RouteTablePublic

    SubnetRouteTableAssociationPublic:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId:
          Ref: RouteTablePublic
        SubnetId:
          Ref: SubnetPublic

    # Step 5: Create a NAT Gateway
    # Before creating NAT Gateway, we need to create Elastic IP with vpc scope
    EIP:
      Type: AWS::EC2::EIP
      Properties:
        Domain: vpc

    NatGateway:
      Type: AWS::EC2::NatGateway
      Properties:
        AllocationId:
          Fn::GetAtt: [EIP, AllocationId]
        SubnetId:
          Ref: SubnetPublic

    RouteTablePrivate:
      Type: AWS::EC2::RouteTable
      Properties:
        VpcId:
          Ref: VPCStaticIP
        Tags:
          - Key: Name
            Value: ${self:service}-${self:provider.stage}-private-route

    RoutePrivate:
      Type: AWS::EC2::Route
      Properties:
        DestinationCidrBlock: 0.0.0.0/0
        NatGatewayId:
          Ref: NatGateway
        RouteTableId:
          Ref: RouteTablePrivate

    SubnetRouteTableMainAssociationPrivate:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId:
          Ref: RouteTablePrivate
        SubnetId:
          Ref: SubnetPrivate

    SubnetRouteTableMainAssociationPrivate2:
      Type: AWS::EC2::SubnetRouteTableAssociation
      Properties:
        RouteTableId:
          Ref: RouteTablePrivate
        SubnetId:
          Ref: SubnetPrivate2

    DatabaseSubnetGroup:
      Type: AWS::RDS::DBSubnetGroup
      Properties:
        DBSubnetGroupDescription: Db subnet for ${self:service}
        SubnetIds:
          - Ref: SubnetPrivate
          - Ref: SubnetPrivate2

    RDSSecurityGroup:
      Type: "AWS::EC2::SecurityGroup"
      Properties:
        GroupDescription: SecurityGroup ${self:service}/${self:provider.stage}
        SecurityGroupIngress:
          - IpProtocol: tcp
            FromPort: 3306
            ToPort: 3306
            SourceSecurityGroupId:
              Fn::GetAtt: [VPCStaticIP, DefaultSecurityGroup]
        VpcId:
          Ref: VPCStaticIP

    ServerlessRDSCluster:
      Type: AWS::RDS::DBCluster
      Properties:
        DBSubnetGroupName:
          Ref: DatabaseSubnetGroup
        Engine: aurora-postgresql
        EngineMode: serverless
        DatabaseName: ${self:service}
        MasterUsername: ${ssm:/${self:service}/${self:provider.stage}/POSTGRES_USERNAME}
        MasterUserPassword: ${ssm:/${self:service}/${self:provider.stage}/POSTGRES_PASSWORD}
        VpcSecurityGroupIds:
          - Ref: RDSSecurityGroup
        ScalingConfiguration:
          AutoPause: False
          MinCapacity: 2
          MaxCapacity: 256

You can find all the options available at .

Example to create an RDS database using :

Read the documentation about .

Read serverless blog post about .

Configure the VPC manually with the console by following this step by step tutorial .

AWS Resource and Property Types Reference
AWS::RDS::DBCluster
Serverless Variables
Managing secrets, API keys and more with Serverless
Configure and Connect to Serverless MySQL Database
CloudFormation
Infrastructure as code
detect drift
You can follow the progress for your changes
You can delete all the resources created and detect drift