Infrastructure as Code: AWS CloudFormation
Tags: aws, devops, tutorial, architecture
Published: 2022-01-24 16:01:55

## 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](https://aws.amazon.com/). Please note that it is advised to follow [security guidelines](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html) 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](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) and [quickly configure](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html) 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
```
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
```
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](https://youtu.be/RXJ2h-DQS18) on how to deploy a template from the AWS console. Or you can upload it through the [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html):
```
aws cloudformation create-stack --stack-name mystack --template-url http://examplebucketname.s3.amazonaws.com/exampletemplate.yaml
```
You can delete the stack by running this from AWS CLI:
```
aws cloudformation delete-stack --stack-name mystack
```

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
      .
      .
      .
```
You can watch a [video tutorial](https://youtu.be/RXJ2h-DQS18) 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
```
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
```
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
```

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