• Infrastructure as Code (IaC) is defining both your infrastructure and code in a single file. You can use tools to “start up” this infrastructure.
  • Amazon’s CloudFormation is one implementation of IaC. You define your infrastructure and code in a YAML file.
  • You can define various entities in your YAML file, such as auto-scaled functions, databases, message queues, pub/sub systems, etc.
  • You can define when your functions run. For example, when a certain endpoint is hit, or when a certain event is published, etc.
  • You can specify env vars that will be present for your functions.
  • You can define databases, and give your function(s) read and/or write access to them.

Introduction

Infrastructure as Code (IaC) is basically defining both your infrastructure and the code that runs on it in a single file. You then use tools to “start up” your infrastructure in the cloud and in effect, deploy your code/system to the cloud. Your system is often auto-scaled.

By “defining your infrastructure”, I mean you list out the cloud components that you need, such as auto-scaled servers, auto-scaled functions (lambda), auto-scaled databases, auto-scaled message queues, auto-scaled pub/sub, you get the idea. You list these things in a file, and then in that same file, you define relationships between them. For example, you might say that a certain auto-scaled function should get triggered when a certain endpoint is hit, or when a certain event occurs in your pub/sub system. Furthermore, you can define a connection between your auto-scaled function and your auto-scaled database, so that the function can read and write to the database.

The best way to learn this, as with most other things, is by looking at an example. One implementation of Infrastructure as Code (IaC) is Amazon’s “CloudFormation” service. You define your infrastructure and code in a YAML file. And then you tell CloudFormation to “start up” (deploy) your infrastructure in the cloud. Let’s take a look at a concrete example.

Amazon CloudFormation

Here is an example YAML file that defines a lambda function and an endpoint (API Gateway) that triggers the function. Ignore everything except the commented lines!

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # we define an auto-scaled function (lambda function)
    Properties:
      Handler: index.handler # here is the path to the file that houses the function and the name of the function
      Runtime: nodejs14.x
      CodeUri: hello-world/
      Events: # here is the event that triggers the function. In this case when the /hello endpoint is hit
        HelloWorldApi:
          Type: Api 
          Properties:
            Path: /hello
            Method: get

In summary, the above example defines a single lambda function that gets executed when the /hello endpoint is hit. The more this endpoint gets hit, the more the lambda function will scale (i.e. more compute resources will be allocated in order to run more instances of the function).

Let’s take a look at another example. This time, we will define a lambda function that gets triggered when a file is uploaded to an S3 bucket. “S3” is just an amazon service that allows you to store files. Ignore everything except the commented lines.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2019-06-15
Resources: # notice we define *2* resources this time
  S3Bucket: # the first one is an S3 bucket (the file storage service)
    Type: AWS::S3::Bucket

  FileUploadedLambda: # the second one is a lambda function
    Type: AWS::Serverless::Function 
    Properties:
      CodeUri: src/ # again, here is the path to the file that houses the function
      Handler: app.lambda_handler # here's the name of the function
      Runtime: python3.8 # this time, we specify the runtime to be python (so this is a python function), but don't worry about this too much
      Events: # PAY ATTENTION HERE: time time, we specify that the function should run when a file is uploaded to the S3 bucket we defined above
        S3FileUpload:
          Type: S3
          Properties:
            Bucket: !Ref S3Bucket
            Events: s3:ObjectCreated:*

In summary, the above example:

  • defines two resources: an S3 bucket (amazon file storage service) and a lambda function
  • specifies that the lambda function should run when a file is uploaded to the S3 bucket

Let’s do a 3rd example, one with a database! What do you say? Well too bad, you have no choice I’m afraid.

Here’s the example. Again, ignore everything except the commented lines.

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Resources:
  HelloFunction: # our first resource is a lambda function
    Type: AWS::Serverless::Function
    Properties:
      Handler: index.handler
      Runtime: nodejs14.x
      CodeUri: path/to/your/code
      Policies: # here we specify that this function should have read access to a DynamoDB table. DynamoDB is just an amazon NoSQL database.
        - DynamoDBReadPolicy:
            TableName: !Ref SampleTable # this is the table it needs read access to (the table is defined further below)
      Environment: # here we are setting an env var on the server that the lambda function runs on
        Variables:
          TABLE_NAME: !Ref SampleTable # we put the name of the table as an env var
      Events:
        HelloApi:
          Type: Api
          Properties:
            Path: /hello
            Method: get

  SampleTable: # here we define the DynamoDb table resource
    Type: AWS::DynamoDB::Table
    Properties:
      TableName: SampleTable # here is the name of the table
      AttributeDefinitions: # here is kinda the "schema" of the table. We are saying objects in this table have a string as the key
        - AttributeName: id
          AttributeType: S
      KeySchema:
        - AttributeName: id
          KeyType: HASH
      BillingMode: PAY_PER_REQUEST # this is just how you want to be charged for using the table, don't worry for now!

In summary, the above example:

  • defines a lambda function and a DynamoDB table (an amazon NoSQL database table)
  • specifies that the lambda function should have read access to the DynamoDB table
  • sets an env var on the server housing the lambda function, such that the lambda function can read the table name from this env var

Let’s define some terms.

Terms

Here are some terms you may encounter out in the wild relating to this stuff.

  • IaC: Infrastructure as Code - Anytime you write down the infrastructure that your system uses along with the code that runs on it, you are doing IaC. The infrastructure is often auto-scaled. You often use a tool provided by the IaC provider to spin up your system. For example, Amazon’s CloudFormation.
  • CloudFormation - Amazon’s IaC service.
  • Lambda function - A function that runs in the cloud. It is auto-scaled. It is often triggered by an event, such as an endpoint being hit, a file being uploaded to S3, or a message being published to an SNS topic.
  • S3 - Amazon’s file storage service. You can store files in it. You can also trigger events when files are uploaded to it.
  • DynamoDB - Amazon’s NoSQL database service. You can store objects in it. You can also trigger events when objects are created, updated, or deleted in it.
  • API Gateway - Amazon’s service that allows you to define endpoints that trigger lambda functions.
  • SNS - Amazon’s pub/sub service. Supports auto-scaling.
  • SQS - Amazon’s message queue service. Supports auto-scaling. Multiple entities can put messages in the queue and multiple entities can take messages out of the queue, however, each message can only be taken out by one entity. This prevents duplicate processing of messages.

Not too bad ay Billy? Super easy to get an auto-scaled system up and running in the cloud using IaC.