Sample Application - New Films in IMDB Top 250 - Part I

Published on 14 April 2022

Introduction

One thing I had been wanting to do for a while, is create a fairly simple serverless application, bringing together several different components from the AWS suite of services, in a way that puts them together to show how they can be used to create a joined-up application in a fairly simple manner.

  • DynamoDB
  • API Gateway
  • Cloudwatch
  • Lambda
  • SES
  • S3
  • Cloudfront
  • Route53

The basic idea was to get users to subscribe via an email address, store them, and then send updates based on changes to the "Top 250" movies in IMDB, so as new films were added, users would be notified.

img

That looks daunting for something "simple", but AWS have a fantastic tool though, the AWS Serverless Application Model (SAM). SAM is a really good tool for taking some of the tools and services mentioned above, and allowing you to turn them into usable building blocks without having to invest a huge amount of time in understanding them all in great detail. SAM is fantastic for allowing you to spend more time thinking about what you are trying to achieve from a functional point of view.

So how do we start? We install SAM. There are a few requirements first before we can do that:

  1. Create an IAM role
  2. Install Docker (if you want to do local testing)

For creating the IAM Role, you probably need permissions similar to this:

        {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Sid": "CloudFormationTemplate",
                    "Effect": "Allow",
                    "Action": [
                        "cloudformation:CreateChangeSet"
                    ],
                    "Resource": [
                        "arn:aws:cloudformation:*:aws:transform/Serverless-2016-10-31"
                    ]
                },
                {
                    "Sid": "CloudFormationStack",
                    "Effect": "Allow",
                    "Action": [
                        "cloudformation:CreateChangeSet",
                        "cloudformation:CreateStack",
                        "cloudformation:DeleteStack",
                        "cloudformation:DescribeChangeSet",
                        "cloudformation:DescribeStackEvents",
                        "cloudformation:DescribeStacks",
                        "cloudformation:ExecuteChangeSet",
                        "cloudformation:GetTemplateSummary",
                        "cloudformation:ListStackResources",
                        "cloudformation:UpdateStack"
                    ],
                    "Resource": [
                        "arn:aws:cloudformation:*:111122223333:stack/*"
                    ]
                },
                {
                    "Sid": "S3",
                    "Effect": "Allow",
                    "Action": [
                        "s3:CreateBucket",
                        "s3:GetObject",
                        "s3:PutObject"
                    ],
                    "Resource": [
                        "arn:aws:s3:::*/*"
                    ]
                },
                {
                    "Sid": "ECRRepository",
                    "Effect": "Allow",
                    "Action": [
                        "ecr:BatchCheckLayerAvailability",
                        "ecr:BatchGetImage",
                        "ecr:CompleteLayerUpload",
                        "ecr:CreateRepository",
                        "ecr:DeleteRepository",
                        "ecr:DescribeImages",
                        "ecr:DescribeRepositories",
                        "ecr:GetDownloadUrlForLayer",
                        "ecr:GetRepositoryPolicy",
                        "ecr:InitiateLayerUpload",
                        "ecr:ListImages",
                        "ecr:PutImage",
                        "ecr:SetRepositoryPolicy",
                        "ecr:UploadLayerPart"
                    ],
                    "Resource": [
                        "arn:aws:ecr:*:111122223333:repository/*"
                    ]
                },
                {
                    "Sid": "ECRAuthToken",
                    "Effect": "Allow",
                    "Action": [
                        "ecr:GetAuthorizationToken"
                    ],
                    "Resource": [
                        "*"
                    ]
                },
                {
                    "Sid": "Lambda",
                    "Effect": "Allow",
                    "Action": [
                        "lambda:AddPermission",
                        "lambda:CreateFunction",
                        "lambda:DeleteFunction",
                        "lambda:GetFunction",
                        "lambda:GetFunctionConfiguration",
                        "lambda:ListTags",
                        "lambda:RemovePermission",
                        "lambda:TagResource",
                        "lambda:UntagResource",
                        "lambda:UpdateFunctionCode",
                        "lambda:UpdateFunctionConfiguration"
                    ],
                    "Resource": [
                        "arn:aws:lambda:*:111122223333:function:*"
                    ]
                },
                {
                    "Sid": "IAM",
                    "Effect": "Allow",
                    "Action": [
                        "iam:AttachRolePolicy",
                        "iam:DeleteRole",
                        "iam:DetachRolePolicy",
                        "iam:GetRole",
                        "iam:TagRole"
                    ],
                    "Resource": [
                        "arn:aws:iam::111122223333:role/*"
                    ]
                },
                {
                    "Sid": "IAMPassRole",
                    "Effect": "Allow",
                    "Action": "iam:PassRole",
                    "Resource": "*",
                    "Condition": {
                        "StringEquals": {
                            "iam:PassedToService": "lambda.amazonaws.com"
                        }
                    }
                },
                {
                    "Sid": "APIGateway",
                    "Effect": "Allow",
                    "Action": [
                        "apigateway:DELETE",
                        "apigateway:GET",
                        "apigateway:PATCH",
                        "apigateway:POST",
                        "apigateway:PUT"
                    ],
                    "Resource": [
                        "arn:aws:apigateway:*::*"
                    ]
                }
            ]
        }

We are giving a fairly wide range of permissions there, so you could limit the resources they are applied too. Additionally, you may need to add DynamoDB permissions too.

More information about permissions can be found here.

Once you have done that, install Docker, and you are good to install SAM-CLI.

Now you have SAM installed, you can create your application. So create a folder and then run sam init:

PS C:\Users\me\SAM\films\sam-app1> sam init

When prompted, choose a AWS Quick Start Template, choose a ZIP package type, and then enter your project name.

image

The sample will then be cloned locally. Once that is done, you just need to choose the template you want. "Hello World Example" is a good choice.

Once complete you should have a sample app within your directory structure. Success! You should have now have a folder structure with (amongst others) a template.yaml, requirements.txt, etc.

In the next part, we will look at some simple edits to the template.yaml that we can do to get started with our project.

comments powered by Disqus