[AWS Basics] CloudFormation concepts

Template

  • YAML hoặc JSON formated text file

  • Có thể save .json, .yaml, .template, .txt,…

  • CloudFormation sử dụng các template nhưng blueprint để xây dựng các AWS resources.

AWSTemplateFormatVersion: 'version date' (optional) # version of the CloudFormation template. Only accepted value is '2010-09-09'

Description: 'String' (optional) # a text description of the Cloudformation template

Metadata: 'template metadata' (optional) # objects that provide additional information about the template

Parameters: 'set of parameters' (optional) # a set of inputs used to customize the template

Rules: 'set of rules' (optional) # a set of rules to validate the parameters provided at deployment/update

Mappings: 'set of mappings' (optional) # a mapping of keys and associated values

Conditions: 'set of conditions' (optional) # conditions that control whether certain resources are created

Transform: 'set of transforms' (optional) # for serverless applications

Resources: 'set of resources' (required) # a components of your infrastructure

Hooks: 'set of hooks' (optional) # Used for ECS Blue/Green Deployments

Outputs: 'set of outputs' (optional) # values that are returned whenever you view your stack's properties

Một số ví dụ đơn giản:

  • Format version: latest version là 2010-09-09 và đang là giá trị valid duy nhất hiện tại:
AWSTemplateFormatVersion: "2010-09-09"
  • Description: cho phép include comment về template
Description: AWS CloudFormation workshop - Resources (uksb-1q9p31idr).
  • Metadata: cung cấp các thông tin cho các tools khác là đang interact với cloudformation template
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: 'Amazon EC2 Configuration'
        Parameters:
          - InstanceType
    ParameterLabels:
      InstanceType:
        default: 'Type of EC2 Instance'
  • Parameter: input custom values cho template
Parameters:
  InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
      - t2.small
    Description: 'Enter t2.micro or t2.small. Default is t2.micro.'
  • Resource: declare resource
Resources:
  WebServerInstance:
    Type: 'AWS::EC2::Instance'
    Properties:
      InstanceType: !Ref InstanceType
      ImageId: <replace with AMI ID ami-xxxxx>

Intrinsic functions

  • Ref:

No Ref:

AmiID:
  Type: AWS::EC2::Image::Id
  Description: 'The ID of the AMI.'

With Ref:

Resources:
  WebServerInstance:
    Type: AWS::EC2::Instance
    Properties:
      # Use !Ref function in ImageId property
      ImageId: !Ref AmiID
      InstanceType: !Ref InstanceType
  • Fn::Join và Fn:Sub: Để giúp quản lý AWS resources, bạn có thể assign metadata cho mỗi resource dưới dạng tags. Bạn có thể sử dụng hàm Fn::Join để tạo tag như sau:
Resources:
  WebServerInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmiID
      InstanceType: !Ref InstanceType
      Tags:
        - Key: Name
          Value: !Join [ '-', [ !Ref InstanceType, webserver ] ]
        - Key: InstanceType
          Value: !Sub ${InstanceType}

Xem thêm các functions tại đây:

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/intrinsic-function-reference.html

Pseudo parameters

  • Là các parameter mà đã được định nghĩa bởi AWS, không declare trong template, sử dụng như argument cho Ref function:
Outputs:
  MyStacksRegion:
    Value: !Ref "AWS::Region"

Xem thêm tại đây: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/pseudo-parameter-reference.html

Mapping

Mappings:
  AnExampleMap:
    TopLevelKey01:
      Key01: Value01
      Key02: Value02

    TopLevelKey02:
      AnotherKey: AnExampleValue

    TopLevelKey03:
      AFinalKey: ADifferentValue

Ví dụ: define 2 môi trường là test và prod:

→ Phần parameter:

Parameters:
  EnvironmentType:
    Description: 'Specify the Environment type of the stack.'
    Type: String
    Default: Test
    AllowedValues:
      - Test
      - Prod
    ConstraintDescription: 'Specify either Test or Prod.'

→ Tạo EnvironmentToInstanceType trong phần mapping:

Mappings:
  EnvironmentToInstanceType: # Map Name
    Test: # Top level key
      InstanceType: t2.micro # Second level key
    Prod:
      InstanceType: t2.small

→ Tiếp theo, modify InstanceType:

Resources:
  WebServerInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmiID
      InstanceType: !FindInMap
        - EnvironmentToInstanceType # Map Name
        - !Ref EnvironmentType # Top Level Key
        - InstanceType # Second Level Key

→ Update Tags:

Tags:
  - Key: Name
    Value: !Join [ '-', [ !Ref EnvironmentType, webserver ] ]

Outputs:

Cho phép get access tới information về resource trong stack

Resources:
  WebServerInstance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref AmiID
      InstanceType: !FindInMap [EnvironmentToInstanceType, !Ref EnvironmentType, InstanceType]
      Tags:
        - Key: Name
          Value: !Join [ '-', [ !Ref EnvironmentType, webserver ] ]

  WebServerEIP:
    Type: 'AWS::EC2::EIP'
    Properties:
      Domain: vpc
      InstanceId: !Ref WebServerInstance

Outputs:
  WebServerPublicDNS:
    Description: 'Public DNS of EC2 instance'
    Value: !GetAtt WebServerInstance.PublicDnsName

  WebServerElasticIP:
    Description: 'Elastic IP assigned to EC2'
    Value: !Ref WebServerEIP

Linting and testing:

cfn-lint:

pip install cfn-lint

taskcat: for testing:

pip install taskcat

cfn-flip: convert JSON → YAML và ngược lại

pip install cfn-flip

Helper scripts:

cfn-init: )(~userdata)

đọc metadata từ AWS::CloudFormation::Init và:

  • Fetch và parse metadata từ CloudFormation

  • Install packages

  • Write files to disk

  • Enable/disable và start/stop services

VD:

Cài đặt HTTPD và PHP packages:

WebServerInstance:
  Type: AWS::EC2::Instance
  Metadata:
    AWS::CloudFormation::Init:
      config:
        packages:
          yum:
            httpd: []
            php: []

Tạo index.php

WebServerInstance:
  Type: AWS::EC2::Instance
  Metadata:
    AWS::CloudFormation::Init:
      config:
        packages: \\
          {...}
        files:
          /var/www/html/index.php:
            content: |
              <!DOCTYPE html>
              <html>
              <body>
                <center>
                  <?php
                  # Get the instance ID from meta-data and store it in the $instance_id variable
                  $url = "<http://169.254.169.254/latest/meta-data/instance-id>";
                  $instance_id = file_get_contents($url);
                  # Get the instance's availability zone from metadata and store it in the $zone variable
                  $url = "<http://169.254.169.254/latest/meta-data/placement/availability-zone>";
                  $zone = file_get_contents($url);
                  ?>
                  <h2>EC2 Instance ID: <?php echo $instance_id ?></h2>
                  <h2>Availability Zone: <?php echo $zone ?></h2>
                </center>
              </body>
              </html>
            mode: 000644
            owner: apache
            group: apache

Start và enable httpd:

WebServerInstance:
  Type: AWS::EC2::Instance
  Metadata:
    AWS::CloudFormation::Init:
      config:
        packages:
          {...}
        files:
          {...}
        services:
          sysvinit:
            httpd:
              enabled: true
              ensureRunning: true

Gọi cfn-init script:

UserData:
  Fn::Base64:
    !Sub |
      #!/bin/bash -xe
      # Update aws-cfn-bootstrap to the latest
      yum install -y aws-cfn-bootstrap
      # Call cfn-init script to install files and packages
      /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region

cfn-hub: detect các thay đổi trong resource metadata và thực hiện các actions khi sự thay đổi được detect. Điều này cho phép bạn cập nhật cấu hình trên các phiên bản Amazon EC2 đang chạy của mình thông qua hành động API UpdateStack.

WebServerInstance:
  Type: AWS::EC2::Instance
  Metadata:
    AWS::CloudFormation::Init:
      config:
        packages:
          {...}
        files:
          /var/www/html/index.php:
            {...}
          /etc/cfn/cfn-hup.conf:
            content: !Sub |
              [main]
              stack=${AWS::StackId}
              region=${AWS::Region}
              interval=1
            mode: 000400
            owner: root
            group: root
          /etc/cfn/hooks.d/cfn-auto-reloader.conf:
            content: !Sub |
              [cfn-auto-reloader-hook]
              triggers=post.update
              path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
              action=/opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
              runas=root
        services:
          {...}

enable và start cfn-hup trong phần services của temaplate:

WebServerInstance:
  Type: AWS::EC2::Instance
  Metadata:
    AWS::CloudFormation::Init:
      config:
        packages:
          {...}
        files:
          /var/www/html/index.php:
            {...}
          /etc/cfn/cfn-hup.conf:
              {...}
          /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              {...}
        services:
          sysvinit:
            httpd:
              enabled: true
              ensureRunning: true
            cfn-hup:
              enabled: true
              ensureRunning: true
              files:
                - /etc/cfn/cfn-hup.conf
                - /etc/cfn/hooks.d/cfn-auto-reloader.conf

Xem thêm: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/cfn-helper-scripts-reference.html

Condition

Parameters:
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  EnvType:
    Description: Specify the Environment type of the stack.
    Type: String
    AllowedValues:
      - test
      - prod
    Default: test
    ConstraintDescription: Specify either test or prod.

Add thêm condition sau vào phần Conditions của template:

Conditions:
  IsProduction: !Equals
    - !Ref EnvType
    - prod

Lúc này ta có thể viết được đoạn code sử dụng condition như sau:

Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: t2.micro

  MountPoint:
    Type: AWS::EC2::VolumeAttachment
    Properties:
      InstanceId: !Ref EC2Instance
      VolumeId: !Ref Volume
      Device: /dev/sdh
    Condition: IsProduction

  Volume:
    Type: AWS::EC2::Volume
    Properties:
      Size: 2
      AvailabilityZone: !GetAtt EC2Instance.AvailabilityZone
      Encrypted: true
    Condition: IsProduction

Định nghĩa condition ở property level:

Giả sử bạn muốn tạo một EC2 instance type là t2.micro cho test env, t2.small cho production env, có thể define condition ở resource property level cho InstanceType property.

Parameters:
  LatestAmiId:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2

  EnvType:
    Description: Specify the Environment type of the stack.
    Type: String
    AllowedValues:
      - test
      - prod
    Default: test
    ConstraintDescription: Specify either test or prod.

Conditions:
  IsProduction: !Equals
    - !Ref EnvType
    - prod

Tại phần resource:

Resources:
  EC2Instance:
    Type: AWS::EC2::Instance
    Properties:
      ImageId: !Ref LatestAmiId
      InstanceType: !If [IsProduction, t2.small, t2.micro]

Resource dependency:

VD1:

Parameters:
  EmailAddress:
    Description: Enter an email address to subscribe to your Amazon SNS topic.
    Type: String

Resources:
  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      Tags:
        - Key: Name
          Value: Resource-dependencies-workshop

  SNSTopicSubscription:
    Type: AWS::SNS::Subscription
    Properties:
      Endpoint: !Ref EmailAddress
      Protocol: email
      TopicArn: !Ref SNSTopic

  SecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Workshop Security Group
      Tags:
        - Key: Name
          Value: Resource-dependencies-workshop

  SecurityGroupIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !GetAtt SecurityGroup.GroupId
      IpProtocol: tcp
      FromPort: 80
      ToPort: 80
      CidrIp: 0.0.0.0/0

VD2:

Resources:
  S3Bucket:
    Type: AWS::S3::Bucket
    Properties:
      Tags:
        - Key: Name
          Value: Resource-dependencies-workshop

  SNSTopic:
    Type: AWS::SNS::Topic
    DependsOn: S3Bucket
    Properties:
      Tags:
        - Key: Name
          Value: Resource-dependencies-workshop

Dynamic references:

Có thể sử dụng dynamic reference trong CloudFormation template để refer tới các value store ở AWS services như SSM,..

Dynamic reference cho Parameter Store:

Xem thêm + làm theo lab ở đây: https://catalog.workshops.aws/cfn101/en-US/intermediate/templates/dynamic-references

Code: git clone <https://github.com/aws-samples/cfn101-workshop\>

Stack:

  • là một deployment của CloudFormation template. Bạn có thể tạo nhiều stack từ 1 template duy nhất.

  • Stack bao gồm một collection các AWS resource mà bạn có thể quản lý như single unit.

Nested stacks:

Nested stack resource:

Resources:
  NestedStackExample:
    Type: AWS::CloudFormation::Stack
    Properties:
      TemplateURL: 'Path/To/Template'
      Parameters:
        ExampleKey: ExampleValue

Tạo nested stack:

main.yml:

AWSTemplateFormatVersion: "2010-09-09"

Description: AWS CloudFormation workshop - Nested stacks - Root template (uksb-1q9p31idr).

Parameters:
  S3BucketName:
    AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-.]*[0-9a-zA-Z])*$
    ConstraintDescription: Bucket name can include numbers, lowercase letters, uppercase letters, periods (.), and hyphens (-). It cannot start or end with a hyphen (-).
    Description: S3 bucket name for the Nested Stacks. S3 bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-).
    Type: String

    AvailabilityZones:
      Type: List<AWS::EC2::AvailabilityZone::Name>
      Description: The list of Availability Zones to use for the subnets in the VPC. Select 2 AZs.

    VPCName:
      Type: String
      Description: The name of the VPC.
      Default: cfn-workshop-vpc

    VPCCidr:
      Type: String
      Description: The CIDR block for the VPC.
      AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
      ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
      Default: 10.0.0.0/16

    PublicSubnet1Cidr:
      Type: String
      Description: The CIDR block for the public subnet located in Availability Zone 1.
      AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
      ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
      Default: 10.0.0.0/24

    PublicSubnet2Cidr:
      Type: String
      Description: The CIDR block for the public subnet located in Availability Zone 2.
      AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$
      ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28
      Default: 10.0.1.0/24


    EnvironmentType:
      Description: 'Specify the Environment type of the stack.'
      Type: String
      Default: Test
      AllowedValues:
        - Dev
        - Test
        - Prod
      ConstraintDescription: 'Specify either Dev, Test or Prod.'

Resources:
#  Create VPC resource in the main template" 
    VpcStack:
      Type: AWS::CloudFormation::Stack
      Properties:
        TemplateURL: !Sub https://${S3BucketName}.s3.amazonaws.com/vpc.yaml
        TimeoutInMinutes: 20
        Parameters:
          AvailabilityZones:
            Fn::Join:
              - ','
              - !Ref AvailabilityZones
          VPCCidr: !Ref VPCCidr
          VPCName: !Ref VPCName
          PublicSubnet1Cidr: !Ref PublicSubnet1Cidr
      PublicSubnet2Cidr: !Ref PublicSubnet2Cidr

# 3. Create IAM resource in the main template" 
    IamStack:
      Type: AWS::CloudFormation::Stack
      Properties:
        TemplateURL: !Sub https://${S3BucketName}.s3.amazonaws.com/iam.yaml
        TimeoutInMinutes: 10

# Create EC2 resource in main template"
    EC2Stack:
      Type: AWS::CloudFormation::Stack
      Properties:
        TemplateURL: !Sub https://${S3BucketName}.s3.amazonaws.com/ec2.yaml
        TimeoutInMinutes: 20
            Parameters:
          EnvironmentType: !Ref EnvironmentType
                VpcId: !GetAtt VpcStack.Outputs.VpcId
          SubnetId: !GetAtt VpcStack.Outputs.PublicSubnet1
                WebServerInstanceProfile: !GetAtt IamStack.Outputs.WebServerInstanceProfile

# Output the `WebsiteURL` in the main template
Outputs:
  WebsiteURL:
    Value: !GetAtt EC2Stack.Outputs.WebsiteURL

ec2.yml

AWSTemplateFormatVersion: "2010-09-09"

Description: AWS CloudFormation workshop - Nested stacks - EC2 template.

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: 'Amazon EC2 Configuration'
        Parameters:
          - AmiID

    ParameterLabels:
      AmiID:
        default: 'Amazon Machine Image ID'

Parameters:
  EnvironmentType:
    Description: 'Specify the Environment type of the stack.'
    Type: String
    Default: Test
    AllowedValues:
      - Dev
      - Test
      - Prod
    ConstraintDescription: 'Specify either Dev, Test or Prod.'

  AmiID:
    Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
    Description: 'The ID of the AMI.'
    Default: /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2
    VpcId:
      Type: AWS::EC2::VPC::Id
      Description: 'The VPC ID'

    SubnetId:
      Type: AWS::EC2::Subnet::Id
      Description: 'The Subnet ID'

    WebServerInstanceProfile:
      Type: String
      Description: 'Instance profile resource ID'

Mappings:
  EnvironmentToInstanceType:
    Dev:
      InstanceType: t2.nano
    Test:
      InstanceType: t2.micro
    Prod:
      InstanceType: t2.small

Resources:
  WebServerInstance:
    Type: AWS::EC2::Instance
    Metadata:
      AWS::CloudFormation::Init:
        config:
          packages:
            yum:
              httpd: []
              php: []
          files:
            /var/www/html/index.php:
              content: |
                <!DOCTYPE html>
                <html>
                <body>
                  <center>
                    <?php
                    # Get the instance ID from meta-data and store it in the $instance_id variable
                    $url = "http://169.254.169.254/latest/meta-data/instance-id";
                    $instance_id = file_get_contents($url);
                    # Get the instance's availability zone from metadata and store it in the $zone variable
                    $url = "http://169.254.169.254/latest/meta-data/placement/availability-zone";
                    $zone = file_get_contents($url);
                    # Get the instance AMI ID and store it in the $ami_id variable
                    $url = "http://169.254.169.254/latest/meta-data/ami-id";
                    $ami_id = file_get_contents($url);
                    ?>
                    <h2>EC2 Instance ID: <?php echo $instance_id ?></h2>
                    <h2>Availability Zone: <?php echo $zone ?></h2>
                    <h2>AMI ID: <?php echo $ami_id ?></h2>
                  </center>
                </body>
                </html>
              mode: 000644
              owner: apache
              group: apache

            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=1
              mode: 000400
              owner: root
              group: root

            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
                runas=root

          services:
            sysvinit:
              httpd:
                enabled: true
                ensureRunning: true

              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf

    Properties:
      SubnetId: !Ref SubnetId
      IamInstanceProfile: !Ref WebServerInstanceProfile
      ImageId: !Ref AmiID
      InstanceType: !FindInMap [EnvironmentToInstanceType, !Ref EnvironmentType, InstanceType]
      SecurityGroupIds:
        - !Ref WebServerSecurityGroup
      Tags:
        - Key: Name
          Value: !Join [ ' ', [ !Ref EnvironmentType, Web Server ] ]
      UserData:
        Fn::Base64:
          !Sub |
          #!/bin/bash -xe
          # Update aws-cfn-bootstrap to the latest
          yum install -y aws-cfn-bootstrap
          # Call cfn-init script to install files and packages
          /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}
          # Call cfn-signal script to send a signal with exit code
          /opt/aws/bin/cfn-signal --exit-code $? --stack ${AWS::StackName} --resource WebServerInstance --region ${AWS::Region}

    CreationPolicy:
      ResourceSignal:
        Count: 1
        Timeout: PT10M

  WebServerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable HTTP and HTTPS access
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
            VpcId: !Ref VpcId

  WebServerEIP:
    Type: AWS::EC2::EIP
    Properties:
      Domain: vpc
      InstanceId: !Ref WebServerInstance

Outputs:
  WebServerPublicDNS:
    Description: 'Public DNS of EC2 instance'
    Value: !GetAtt WebServerInstance.PublicDnsName

  WebServerElasticIP:
    Description: Elastic IP associated with the web server EC2 instance
    Value: !Ref WebServerEIP

  WebsiteURL:
    Value: !Sub http://${WebServerEIP}
    Description: Application URL

vpc.yml

AWSTemplateFormatVersion: "2010-09-09"

Description: AWS CloudFormation workshop - Nested stacks - VPC template.

Parameters:
  AvailabilityZones:
    Type: List<AWS::EC2::AvailabilityZone::Name>
    Description: The list of Availability Zones to use for the subnets in the VPC.

  VPCName:
    Type: String
    Description: The name of the VPC.

  VPCCidr:
    Type: String
    Description: The CIDR block for the VPC.
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28

  PublicSubnet1Cidr:
    Type: String
    Description: The CIDR block for the public subnet located in Availability Zone 1.
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28

  PublicSubnet2Cidr:
    Type: String
    Description: The CIDR block for the public subnet located in Availability Zone 2.
    AllowedPattern: ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\\/([0-9]|[1-2][0-9]|3[0-2]))$
    ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/16-28

Resources:
  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCCidr
      EnableDnsHostnames: true
      EnableDnsSupport: true
      InstanceTenancy: default
      Tags:
        - Key: Name
          Value: !Ref VPCName

  VPCPublicSubnet1:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PublicSubnet1Cidr
      VpcId: !Ref VPC
      AvailabilityZone: !Select [0, !Ref AvailabilityZones]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: PublicSubnet1
        - Key: subnet-type
          Value: Public

  VPCPublicSubnet1RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: PublicSubnet1

  VPCPublicSubnet1RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VPCPublicSubnet1RouteTable
      SubnetId:
        Ref: VPCPublicSubnet1

  VPCPublicSubnet1DefaultRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId:
        Ref: VPCPublicSubnet1RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId:
        Ref: VPCIGW
    DependsOn:
      - VPCGW

  VPCPublicSubnet2:
    Type: AWS::EC2::Subnet
    Properties:
      CidrBlock: !Ref PublicSubnet2Cidr
      VpcId: !Ref VPC
      AvailabilityZone: !Select [1, !Ref AvailabilityZones]
      MapPublicIpOnLaunch: true
      Tags:
        - Key: Name
          Value: PublicSubnet2
        - Key: subnet-type
          Value: Public

  VPCPublicSubnet2RouteTable:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId: !Ref VPC
      Tags:
        - Key: Name
          Value: PublicSubnet2

  VPCPublicSubnet2RouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      RouteTableId:
        Ref: VPCPublicSubnet2RouteTable
      SubnetId:
        Ref: VPCPublicSubnet2

  VPCPublicSubnet2DefaultRoute:
    Type: AWS::EC2::Route
    Properties:
      RouteTableId: !Ref VPCPublicSubnet2RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref VPCIGW
    DependsOn:
      - VPCGW

  VPCIGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: VPCIGW

  VPCGW:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties:
      VpcId:
        Ref: VPC
      InternetGatewayId:
        Ref: VPCIGW
Outputs:
  VpcId:
    Value: !Ref VPC

  PublicSubnet1:
    Value: !Ref VPCPublicSubnet1

  PublicSubnet2:
    Value: !Ref VPCPublicSubnet2

iam.yml

AWSTemplateFormatVersion: "2010-09-09"

Description: AWS CloudFormation workshop - Nested stacks - IAM template.

Resources:
    SSMIAMRole:
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument:
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - ec2.amazonaws.com
              Action:
                - sts:AssumeRole
        ManagedPolicyArns:
          - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

    WebServerInstanceProfile:
      Type: AWS::IAM::InstanceProfile
      Properties:
        Path: /
        Roles:
          - !Ref SSMIAMRole
Outputs:
  WebServerInstanceProfile:
    Value: !Ref WebServerInstanceProfile

Layered stacks

Ví dụ, nếu bạn có kế hoạch cho nhiều workload deployed bằng nhiều template nhưng mỗi EC2 instance thì đều expected enable SSM. Tương tự như thế, thì bạn muốn deploy 1 VPC bằng 1 stack và sau đó sử dụng nó trong nhiều stacks và workload. Điều này sẽ không đạt được khi sử dụng Nested Stack → bạn cần dùng Layered stack.

Với Layered stack thì sẽ sử dụng Export để tạo global variables có thể import vào bất cứ stack nào.

Ví dụ:

Outputs:
  VpcId:
    Value: !Ref VPC
    Export:
      Name: cfn-workshop-VpcId

  PublicSubnet1:
    Value: !Ref VPCPublicSubnet1
    Export:
      Name: cfn-workshop-PublicSubnet1

  PublicSubnet2:
    Value: !Ref VPCPublicSubnet2
    Export:
      Name: cfn-workshop-PublicSubnet2

và import trong ec2.yml:

WebServerSecurityGroup:
  Type: AWS::EC2::SecurityGroup
  Properties:
    GroupDescription: Enable HTTP and HTTPS access
    SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
    SecurityGroupEgress:
      - IpProtocol: tcp
        FromPort: 80
        ToPort: 80
        CidrIp: 0.0.0.0/0
      - IpProtocol: tcp
        FromPort: 443
        ToPort: 443
        CidrIp: 0.0.0.0/0
    VpcId: !ImportValue cfn-workshop-VpcId

Deploy các package

Refer tới local files:

PythonFunction:
  Type: AWS::Lambda::Function
  Properties:
    FunctionName: cfn-workshop-python-function
    Description: Python Function to return specific TimeZone time
    Runtime: python3.8
    Role: !GetAtt LambdaBasicExecutionRole.Arn
    Handler: lambda_function.handler
    Code: lambda/                                 # <<< This is a local directory
  • Zips up the local file

  • Upload lên S3 bucket

  • Generate một template mới, local path sẽ được thay thế bằng S3 URI

File template mới:

PythonFunction:
  Type: AWS::Lambda::Function
  Properties:
    FunctionName: cfn-workshop-python-function
    Description: Python Function to return specific TimeZone time
    Runtime: python3.8
    Role:
      Fn::GetAtt:
      - LambdaBasicExecutionRole
      - Arn
    Handler: lambda_function.handler
    Code:
      S3Bucket: example-bucket-name
      S3Key: cfn-workshop-package-deploy/1234567890
    TracingConfig:
      Mode: Active

Policy as code:

Tham khảo và làm demo tại: https://catalog.workshops.aws/cfn101/en-US/intermediate/templates/policy-as-code-with-guard

Stack Policy:

Parameters:
  SNSTopicTagValue:
    Description: Tag value for your Amazon SNS topic
    Type: String
    Default: Topic-Tag-1
    MinLength: 1
    MaxLength: 256
Resources:
  SNSTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: Topic-1
      Tags:
        - Key: TagSNS
          Value: !Ref SNSTopicTagValue
Resources:
  SNSTopic:
    DeletionPolicy: Retain
    Type: AWS::SNS::Topic
    Properties:
      TopicName: Topic-2

Change sets:

Khi update stack và cần xem trước các thay đổi thfi sẽ tạo change set trước khi apply thay đổiStack set:

  • Cho phép tạo stack cross regions bằng 1 CloudFormation template

  • Stack set là regional resource