CloudFormation - Tutorial with an EC2 instance

Page content

Important terminology

Stack

A stack is a collection of AWS resources that you can manage as a single unit. In other words, you can create, update, or delete a collection of resources by creating, updating, or deleting stacks.

Resource

Unbreakable components, like an EC2 instances, a routing table, VPC, Lambda function, etc.

The form of template file

The official document was quite comprehensive for me.

Parameters:
  myparam:
    Type: String
    Default: foo
  ...

Resources:
  myResourceName:
    Type: '{{ Resouce_name_defined_by_AWS }}'
    // depending on the resouces ...
    // https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html

Outputs:
  myOutputLogicalID:
    Description: sample output value
    Value: foo
    Export:
      Name: thisIsMyName

Here is the link to the official resources refreence.

Hands-on

Example 1: EC2 instance

Before deploy this stack, you have to create an EC2 keypair, whose name is test-key.

ec2-cloudformation.yaml:

AWSTemplateFormatVersion: '2010-09-09'
Description: Simple EC2 deploy by CloudFormation

Parameters:
  InstanceType:
    Type: String
    Default: t2.micro
    AllowedValues:
      - t2.micro
    Description: This is a demo instance.
  KeyPair:
    Description: Select KeyPair Name.
    Type: AWS::EC2::KeyPair::KeyName

Resources:
  cfnVpc:
    Type: 'AWS::EC2::VPC'
    Properties:
      CidrBlock: '10.0.0.0/16'
      Tags:
        - Key: 'Name'
          Value: 'sample-vpc'

  cfnSubnet:
    Type: 'AWS::EC2::Subnet'
    Properties:
      CidrBlock: '10.0.2.0/24'
      MapPublicIpOnLaunch: true
      Tags:
        - Key: 'Name'
          Value: 'sample-subnet'
      VpcId: !Ref cfnVpc

  cfnInternetGateway:
    Type: 'AWS::EC2::InternetGateway'
    Properties:
      Tags:
      - Key: 'Name'
        Value: 'sample-ig'
  cfnAttachGateway:
    Type: 'AWS::EC2::VPCGatewayAttachment'
    Properties:
      VpcId: !Ref cfnVpc
      InternetGatewayId: !Ref cfnInternetGateway
  cfnRouteTable:
    Type: 'AWS::EC2::RouteTable'
    Properties:
      Tags:
        - Key: 'Name'
          Value: 'sample-route-table'
      VpcId: !Ref cfnVpc
  cfnRoute:
    Type: 'AWS::EC2::Route'
    DependsOn: cfnInternetGateway
    Properties:
      RouteTableId: !Ref cfnRouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref cfnInternetGateway
  cfnSubnetRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId: !Ref cfnSubnet
      RouteTableId: !Ref cfnRouteTable

  cfnEC2Instance:
    Type: 'AWS::EC2::Instance'
    Properties:
      ImageId: "ami-05d34d340fb1d89e5"
      InstanceType: !Ref InstanceType
      SubnetId: !Ref cfnSubnet
      BlockDeviceMappings:
        - DeviceName: '/dev/xvda'
          Ebs:
            VolumeType: 'gp2'
            VolumeSize: 8
      Tags:
        - Key: 'Name'
          Value: 'sample-instance'
      SecurityGroupIds:
        - !Ref cfnSecurityGroup
      KeyName: !Ref KeyPair
  cfnSecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties:
      GroupDescription: "cfnSecurityGroup"
      VpcId: !Ref cfnVpc
      Tags:
        - Key: 'Name'
          Value: 'sample-sg-ssh'
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '22'
        ToPort: '22'
        CidrIp: 0.0.0.0/0

conf.json:

[
    { "ParameterKey": "KeyPair", "ParameterValue": "test-key" }
]

Deploy:

aws cloudformation create-stack \
	--stack-name "MyEC2Stack" \
	--template-body file://ec2-cloudformation.yaml \
	--parameters file://conf.json

Example 2: Deploy minimal OpenSearch

I followed the official document:

AWSTemplateFormatVersion: '2010-09-09'
Description: Simple OpenSearch deploy by CloudFormation

Resources:
  OpenSearchServiceDomain:
    Type: AWS::OpenSearchService::Domain
    Properties:
      DomainName: 'atlex00-test'
      EngineVersion: 'OpenSearch_1.1'
      ClusterConfig:
        DedicatedMasterEnabled: true
        InstanceCount: '2'
        ZoneAwarenessEnabled: true
        InstanceType: 't3.medium.search'
        DedicatedMasterType: 't3.medium.search'
        DedicatedMasterCount: '3'
      EBSOptions:
        EBSEnabled: true
        Iops: '0'
        VolumeSize: '20'
        VolumeType: 'gp2'
      AccessPolicies:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              AWS: '{{ my_user_arn }}'
            Action: 'es:*'
            Resource: 'arn:aws:es:eu-central-1:************:atlex00-test/*'

Deploy:

aws cloudformation create-stack \
	--stack-name "atlex00-test-OpenSearchStack" \
	--template-body file://opensearch.yaml \

CAUTION

When I changed volume size of the EC2 instance and re-deployed, CloudFormation destroied the old instance and created again a new instance from scratch, which means, I lost all configuration inside the EC2. You need to keep the configuration of the instance (for example) with Ansible.

cf. Package

https://awscli.amazonaws.com/v2/documentation/api/latest/reference/cloudformation/package.html