Infrastructure as Code: AWS CloudFormation

ogambakerubo

Hilda Ogamba

Posted on January 24, 2022

Infrastructure as Code: AWS CloudFormation

Introduction

Infrastructure as code (IaC) is the process of provisioning and managing your cloud computing services by coming up with a template file. This lets you reliably and efficiently replicate environment architecture on-demand. AWS CloudFormation is the built-in IaC service. To provision these resources on AWS, you can describe them in a text file with either a .json, .yaml, .template, or .txt extension.

Prerequisites

You will need an active AWS account. Please note that it is advised to follow security guidelines when creating an AWS account to prevent unauthorized access. To follow along from the terminal/command line, you can follow these steps to install AWS CLI and quickly configure the basic settings on your computer.

Benefits of AWS CloudFormation

  • An infrastructure as code template acts as a clear record of resources that you have provisioned.
  • If you accidentally change the the wrong settings while deploying resources, it may break things. IaC helps solve this, especially when it is combined with version control, such as Git.
  • IaC is also reusable meaning that one comprehensive template can be utilized multiple times, in multiple regions, making it much easier to horizontally scale.
  • By creating IaC that follows proper security guidelines you can reuse it with the assurance that the resource settings are secure.
  • CloudFormation can verify that the architecture was successfully deployed, and if there is a failure it can gracefully roll-back the infrastructure to a previously stable state.

AWS CloudFormation concepts

There are three main concepts in CloudFormation:

a) Template: This is the text file that describes your architecture. The template is then used to deploy these resources either through the CLI or the console.
Here is an example of a YAML template. This describes a VPC, one public subnet, an EC2 instance (running a web server) and other dependent resources:

AWSTemplateFormatVersion: 2010-09-09

Description: Template example

# VPC with public subnet and Internet Gateway

Parameters:

  ExampleVpcCidr:
    Type: String
    Default: 10.0.0.0/20

  PublicSubnetCidr:
    Type: String
    Default: 10.0.0.0/24

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


Resources:

###########
# VPC with Internet Gateway
###########

  ExampleVPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref ExampleVpcCidr
      EnableDnsSupport: true
      EnableDnsHostnames: true
      Tags:
        - Key: Name
          Value: Example VPC

  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags:
        - Key: Name
          Value: Example IGW

  VPCtoIGWConnection:
    Type: AWS::EC2::VPCGatewayAttachment
    DependsOn:
      - IGW
      - ExampleVPC
    Properties:
      InternetGatewayId: !Ref IGW
      VpcId: !Ref ExampleVPC

###########
# Public Route Table
###########

  PublicRouteTable:
    Type: AWS::EC2::RouteTable
    DependsOn: ExampleVPC
    Properties:
      VpcId: !Ref ExampleVPC
      Tags:
        - Key: Name
          Value: Public Route Table

  PublicRoute:
    Type: AWS::EC2::Route
    DependsOn:
      - PublicRouteTable
      - IGW
    Properties:
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref IGW
      RouteTableId: !Ref PublicRouteTable

###########
# Public Subnet
###########

  PublicSubnet:
    Type: AWS::EC2::Subnet
    DependsOn: ExampleVPC
    Properties:
      VpcId: !Ref ExampleVPC
      MapPublicIpOnLaunch: true
      CidrBlock: !Ref PublicSubnetCidr
      AvailabilityZone: !Select 
        - 0
        - !GetAZs 
          Ref: AWS::Region
      Tags:
        - Key: Name
          Value: Public Subnet

  PublicRouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    DependsOn:
      - PublicRouteTable
      - PublicSubnet
    Properties:
      RouteTableId: !Ref PublicRouteTable
      SubnetId: !Ref PublicSubnet


###########
# App Security Group
###########

  AppSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    DependsOn: ExampleVPC
    Properties:
      GroupName: App
      GroupDescription: Enable access to App
      VpcId: !Ref ExampleVPC
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 443
          ToPort: 443
          CidrIp: 0.0.0.0/0
      Tags:
        - Key: Name
          Value: App

###########
# EC2 Instance
###########

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.micro
      ImageId: !Ref AmazonLinuxAMIID
      SubnetId: !Ref PublicSubnet
      SecurityGroupIds:
        - !Ref AppSecurityGroup
      Tags:
        - Key: Name
          Value: App Server
      UserData:
        Fn::Base64: |
          #!/bin/bash
          yum update -y
          yum install -y httpd.x86_64
          systemctl start httpd.service
          systemctl enable httpd.service
          echo “Hello from $(hostname -f)” > /var/www/html/index.html

###########
# Outputs
###########

Outputs:

  PublicDnsName:
    Description: EC2 Instance Public DNS Name
    Value: !GetAtt Instance.PublicDnsName
Enter fullscreen mode Exit fullscreen mode

This particular template is divided into 5 sections:

  1. AWSTemplateFormatVersion: This is a string that describes the AWS CloudFormation template version that the template conforms to. It is denoted as a "version date" format.
  2. Description: It is a string that gives a short summary of the content of the template.
  3. Parameters: It contains the values that are passed at runtime.
  4. Resources: This is the most important (and compulsory) section on a template. It basically describes all the resources to be deployed and their properties.
  5. Outputs: These are values that are returned once a stack is created or updated. The outputs can be viewed from the AWS console. Or by running this command from your terminal/command line:
aws cloudformation describe-stacks --stack-name mystack --query "Stacks[0].Outputs" --output text
Enter fullscreen mode Exit fullscreen mode

In this example, the output returns the public DNS name of the EC2 instance. You can copy this DNS name to your browser and it should direct you to a simple web page.

b) Stack: Once you upload this template, these resources are deployed as a single unit referred to as a stack. You can make changes to resources in a stack and in-case of any failures, these changes can be rolled-back. You can also delete a stack and therefore all the resources in a stack. When creating a new stack, make sure give each stack a unique name. You can watch a video tutorial on how to deploy a template from the AWS console. Or you can upload it through the AWS CLI:

aws cloudformation create-stack --stack-name mystack --template-url http://examplebucketname.s3.amazonaws.com/exampletemplate.yaml
Enter fullscreen mode Exit fullscreen mode

You can delete the stack by running this from AWS CLI:

aws cloudformation delete-stack --stack-name mystack
Enter fullscreen mode Exit fullscreen mode

c) Change Set: When updating a resource, you can simply upload an updated template and let CloudFormation make the necessary changes. This can be risky especially if the changes may delete or modify critical resources. A safer alternative is to use change sets. Change sets allow you to preview the proposed modifications to your current architecture before implementing them. We'll make a modification to our template switching the EC2 instance type from t2.micro to t2.small:

###########
# EC2 Instance
###########

  Instance:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: t2.small
      .
      .
      .
Enter fullscreen mode Exit fullscreen mode

You can watch a video tutorial on how to deploy a change set from the AWS console. Or you can upload the change stack through the CLI:

aws cloudformation create-change-set --stack-name mystack --change-set-name changeset-update --template-url http://examplebucketname.s3.amazonaws.com/updatedexampletemplate.yaml
Enter fullscreen mode Exit fullscreen mode

To view the changes to be implemented in a change set, run this from the terminal:

aws cloudformation describe-change-set --stack-name mystack --change-set-name changeset-update
Enter fullscreen mode Exit fullscreen mode

To execute a change set, run this command in the AWS CLI:

aws cloudformation execute-change-set --stack-name mystack --change-set-name changeset-update
Enter fullscreen mode Exit fullscreen mode

Conclusion

Hope this helps you get started!
PS: Don't forget to delete your stack once you are through to avoid extra charges.

💖 💪 🙅 🚩
ogambakerubo
Hilda Ogamba

Posted on January 24, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related