How to launch an EC2 instance with AWS CloudFormation

How to launch an EC2 instance with AWS CloudFormation

What is CloudFormation?

AWS CloudFormation is a service that enables you to define and provision your AWS Infrastructure as Code (IaC). Other famous Infrastructure as Code services are Terraform, Puppet or Chef. CloudFormation allows you to treat your infrastructure as code, meaning you can version-control and automate the provisioning and management of your resources, which reduces the chances of manual errors and streamlines the deployment process.

Template

A template is a JSON or YAML file that contains configuration information about the AWS resources you want to deploy. To reduce the chances of errors due to unnecessary spaces, I prefer using JSON file format.

Within the templates you use following building blocks:

  • Parameters: Parameters are used to parameterize your template. Things like Passwords, InstanceNames, etc. will be used as parameters.
  • Mappings: The optional Mappings section matches a key to a corresponding set of named values. For example when the user chooses as Parameter the InstanceType t2.micro, then we can have a Mapping, which maps this InstanceType to the correct AMI-ID.
  • Resources: The required Resources section declares the AWS resources that you want to deploy, such as an Amazon EC2 instance or an Amazon S3 bucket.
  • Outputs: The optional Outputs section declares output values that you can import into other stacks (to create cross-stack references), return in response (to describe stack calls), or view on the AWS CloudFormation console. For example, you can output the EC2 instance id to make the instance easier to find.
    {
    "AWSTemplateFormatVersion": "2010-09-09",
    "Description": "Creates an Amazon EC2 instance with attached security groups.",
    "Parameters": {
      "KeyName": {
        "Description": "Name of an existing EC2 KeyPair to enable SSH access to the instance",
        "Type": "AWS::EC2::KeyPair::KeyName",
        "ConstraintDescription": "must be the name of an existing EC2 KeyPair."
      },
      "InstanceType": {
        "Description": "WebServer EC2 instance type",
        "Type": "String",
        "Default": "t2.micro",
        "AllowedValues": [
          "t2.micro",
          "t2.medium",
          "m6g.xlarge"
        ],
        "ConstraintDescription": "must be a valid EC2 instance type."
      },
      "VpcID": {
              "Type": "AWS::EC2::VPC::Id",
              "Description": "VPC ID on which EC2 runner instance will reside"
          },
      "SubnetIds": {
          "Type": "List<AWS::EC2::Subnet::Id>",
          "Description": "Use Private App Subnets"
      },
      "InstanceName": {
              "Type": "String",
              "Default": "gitlab-runner",
              "Description": "Name of the runner instance being created"
      },
      "VolumeSize": {
          "Type": "Number",
          "Default": 200,
          "Description": "Volume size"
      },
      "VolumeType": {
          "Type": "String",
          "Default": "gp2",
          "Description": "Volume Type Attached to instance"
      }
    },
    "Mappings": {
      "AWSInstanceType2Arch": {
        "t2.micro": {
          "Arch": "HVM64"
        },
        "t2.medium": {
          "Arch": "HVM64"
        },
        "m6g.xlarge": {
          "Arch": "ARM64"
        }
      },
      "AWSRegionArch2AMI": {
        "eu-central-1": {
          "HVM64": "ami-04e601abe3e1a910f",
          "ARM64": "ami-0329d3839379bfd15"
        }
      }
    },
    "Resources": {
      "EC2Instance": {
        "Type": "AWS::EC2::Instance",
        "Properties": {
          "InstanceType": {
            "Ref": "InstanceType"
          },
          "SecurityGroupIds": [
            "sg-0000001",
            "sg-0000002",
            "sg-0000003"
          ],
          "KeyName": {
            "Ref": "KeyName"
          },
          "ImageId": {
            "Fn::FindInMap": [
              "AWSRegionArch2AMI",
              {
                "Ref": "AWS::Region"
              },
              {
                "Fn::FindInMap": [
                  "AWSInstanceType2Arch",
                  {
                    "Ref": "InstanceType"
                  },
                  "Arch"
                ]
              }
            ]
          },
          "BlockDeviceMappings": [
              {
                  "DeviceName": "/dev/sda1",
                  "Ebs": {
                      "VolumeSize": { "Ref": "VolumeSize"},
                      "VolumeType": { "Ref": "VolumeType"},
                      "Encrypted": false
                  }
              }
          ]
        }
      }
    },
    "Outputs": {
      "InstanceId": {
        "Description": "InstanceId of the newly created EC2 instance",
        "Value": {
          "Ref": "EC2Instance"
        }
      },
      "AZ": {
        "Description": "Availability Zone of the newly created EC2 instance",
        "Value": {
          "Fn::GetAtt": [
            "EC2Instance",
            "AvailabilityZone"
          ]
        }
      },
      "PublicDNS": {
        "Description": "Public DNSName of the newly created EC2 instance",
        "Value": {
          "Fn::GetAtt": [
            "EC2Instance",
            "PublicDnsName"
          ]
        }
      },
      "PublicIP": {
        "Description": "Public IP address of the newly created EC2 instance",
        "Value": {
          "Fn::GetAtt": [
            "EC2Instance",
            "PublicIp"
          ]
        }
      }
    }
    }
    

Helpful tools:

  1. AWS Sample templates
  2. Designer (Can convert JSON to YAML)

Conclusion

AWS CloudFormation is an Infrastructure as Code (IaC) service, which helps you to streamline your deployments, reduces errors and provides version control for consistent resource management. In this example we have learned how to create a template to spin-up an EC2 instance with Security Groups attached and multiple adjustable parameters.