├── .gitignore ├── website ├── squirrel.png ├── index.html ├── website.yaml └── app.js ├── img └── sam-screenshot.png ├── api ├── beta.json ├── package.json ├── buildspec.yml ├── saml.yaml └── index.js ├── upload_website.sh ├── pipeline ├── main.yaml ├── pipeline-roles.yaml └── pipeline.yaml ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /website/squirrel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-serverless-samfarm/HEAD/website/squirrel.png -------------------------------------------------------------------------------- /img/sam-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-serverless-samfarm/HEAD/img/sam-screenshot.png -------------------------------------------------------------------------------- /api/beta.json: -------------------------------------------------------------------------------- 1 | { 2 | "Parameters": { 3 | "SamMultipler": "1", 4 | "OriginUrl": "*" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /api/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-serverless-samfarm-api-demo", 3 | "version": "1.0.0", 4 | "dependencies": { 5 | "moment": "^2.16.0" 6 | }, 7 | "description": "Sam Farm lambda function for Re:Invent 2016 Serverless miniconf", 8 | "main": "index.js", 9 | "scripts": { 10 | "test": "echo \"Error: no test specified\" && exit 1" 11 | }, 12 | "author": "roryj@amazon.com" 13 | } 14 | -------------------------------------------------------------------------------- /api/buildspec.yml: -------------------------------------------------------------------------------- 1 | version: 0.0 2 | environment_variables: 3 | plaintext: 4 | "INPUT_FILE": "saml.yaml" 5 | "S3_BUCKET": "" 6 | containers: 7 | LambdaFunctions: 8 | phases: 9 | during_build: 10 | commands: 11 | - npm install 12 | - aws cloudformation package --template $INPUT_FILE --s3-bucket $S3_BUCKET --output-template post-saml.yaml 13 | artifacts: 14 | files: 15 | - post-saml.yaml 16 | - beta.json -------------------------------------------------------------------------------- /upload_website.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | function usage { 4 | echo """ 5 | This script is to upload the website components of the AWS Serverless Samfarm demo to the s3 bucket created to host 6 | the website. To use, pass the s3 bucket name after the script name. 7 | 8 | upload_website.sh 9 | 10 | ex: 11 | upload_website.sh samfarm-app-demo-app-bucket 12 | """ 13 | } 14 | 15 | S3_BUCKET='' 16 | 17 | # Get the table name 18 | if [ $# -eq 0 ]; then 19 | usage; 20 | exit; 21 | else 22 | S3_BUCKET="$1" 23 | fi 24 | 25 | aws s3 cp ./website/ "s3://$S3_BUCKET" --recursive --exclude "*.yaml" 26 | -------------------------------------------------------------------------------- /website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | SAM's Friendly Farm 4 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /api/saml.yaml: -------------------------------------------------------------------------------- 1 | Transform: 'AWS::Serverless-2016-10-31' 2 | Parameters: 3 | SamMultipler: 4 | Description: "SAM multiplier. Make this really big to have a party :)" 5 | Type: "String" 6 | OriginUrl: 7 | Description: "The origin url to allow CORS requests from. This will be the base URL of your static SAM website." 8 | Type: "String" 9 | Resources: 10 | GetSAMPartyCount: 11 | Type: AWS::Serverless::Function 12 | Properties: 13 | Handler: index.handler 14 | Runtime: nodejs4.3 15 | CodeUri: ./ 16 | Environment: 17 | Variables: 18 | SAM_MULTIPLIER: !Ref SamMultipler 19 | ORIGIN_URL: !Ref OriginUrl 20 | Events: 21 | GetResource: 22 | Type: Api 23 | Properties: 24 | Path: /sam 25 | Method: get 26 | -------------------------------------------------------------------------------- /website/website.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: '2010-09-09' 2 | Description: Template for the S3 static website 3 | Resources: 4 | S3Bucket: 5 | Type: AWS::S3::Bucket 6 | Properties: 7 | AccessControl: PublicRead 8 | WebsiteConfiguration: 9 | IndexDocument: index.html 10 | DeletionPolicy: Retain 11 | S3WebsiteBucketPolicy: 12 | Type: AWS::S3::BucketPolicy 13 | Properties: 14 | Bucket: !Ref S3Bucket 15 | PolicyDocument: 16 | Statement: 17 | - 18 | Action: 19 | - "s3:GetObject" 20 | Effect: "Allow" 21 | Resource: !Sub "arn:aws:s3:::${S3Bucket}/*" 22 | Principal: "*" 23 | Outputs: 24 | BucketName: 25 | Value: !Ref S3Bucket 26 | Description: The created bucket name 27 | WebsiteURL: 28 | Value: !GetAtt S3Bucket.WebsiteURL 29 | Description: URL for the website hosted on S3 30 | S3BucketSecureURL: 31 | Value: !Sub 32 | - https://${Domain} 33 | - Domain: !GetAtt S3Bucket.DomainName 34 | Description: Name of the S3 bucket to hold website content 35 | -------------------------------------------------------------------------------- /api/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var moment = require('moment'); 3 | 4 | exports.handler = (event, context, callback) => { 5 | 6 | var originURL = process.env.ORIGIN_URL || '*'; 7 | 8 | emitLambdaAge(); 9 | 10 | // This variable can be updated and checked in to your repository 11 | // to update the number of SAM squirrels on the screen. 12 | var samCount = 1; 13 | 14 | // Or you can update your Lambda function's environment variable. 15 | var samMultiplier = process.env.SAM_MULTIPLIER || 1; 16 | 17 | var totalSAMs = samCount * samMultiplier; 18 | 19 | console.log('The number of SAMs to show: ' + samCount); 20 | console.log('Multiplier to apply to SAMs: ' + samMultiplier); 21 | console.log('Total number of SAMs to show: ' + totalSAMs); 22 | 23 | callback(null, { 24 | "statusCode": 200, 25 | "body": totalSAMs, 26 | "headers": 27 | { 28 | "Access-Control-Allow-Headers": "Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token", 29 | "Access-Control-Allow-Methods": "GET,OPTIONS", 30 | "Access-Control-Allow-Origin": originURL 31 | } 32 | }); 33 | } 34 | 35 | function emitLambdaAge() { 36 | var now = moment(); 37 | var lambdaAnnouncement = moment('2014-11-04'); 38 | 39 | var daysOld = now.diff(lambdaAnnouncement, 'days'); 40 | 41 | console.log('Lambda is ' + daysOld + ' days old!'); 42 | } 43 | -------------------------------------------------------------------------------- /pipeline/main.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: "2010-09-09" 2 | Description: "Main template for a CI/CD Serverless application." 3 | Parameters: 4 | AppName: 5 | Type: String 6 | Description: Name of the application. 7 | MinLength: "1" 8 | MaxLength: "80" 9 | AllowedPattern: "[A-Za-z0-9-]+" 10 | ConstraintDescription: Malformed input parameter. AppName must only contain upper and lower case letters, numbers, and -. 11 | SAMInputFile: 12 | Type: String 13 | Description: The filename for the SAM file. 14 | Default: saml.yaml 15 | SAMOutputFile: 16 | Type: String 17 | Description: The filename for the output SAM file from the buildspec file. 18 | Default: post-saml.yaml 19 | StagingFile: 20 | Type: String 21 | Description: The cloudformation staging file. Leave empty if no staging file is needed. 22 | Default: beta.json 23 | CodeBuildImage: 24 | Type: String 25 | Default: "aws/codebuild/nodejs:7.0.0" 26 | Description: Image used for CodeBuild project. 27 | GitHubRepoName: 28 | Type: String 29 | Description: The GitHub repo name 30 | GitHubRepoBranch: 31 | Type: String 32 | Description: The GitHub repo branch code pipelines should watch for changes on 33 | Default: master 34 | GitHubUser: 35 | Type: String 36 | Description: GitHub UserName. This username must have access to the GitHubToken. 37 | GitHubToken: 38 | NoEcho: true 39 | Type: String 40 | Description: "Secret. OAuthToken with access to Repo. Long string of characters and digits. Go to https://github.com/settings/tokens" 41 | Resources: 42 | ServerlessRoles: 43 | Type: "AWS::CloudFormation::Stack" 44 | Properties: 45 | Parameters: 46 | AppName: !Ref AppName 47 | TemplateURL: "https://awscomputeblogimages.s3-us-west-2.amazonaws.com/samfarm-pipeline-roles.yaml" 48 | ServerlessPipeline: 49 | Type: "AWS::CloudFormation::Stack" 50 | DependsOn: [ServerlessRoles] 51 | Properties: 52 | Parameters: 53 | AppName: !Ref AppName 54 | SAMInputFile: !Ref SAMInputFile 55 | SAMOutputFile: !Ref SAMOutputFile 56 | StagingFile: !Ref StagingFile 57 | CodeBuildImage: !Ref CodeBuildImage 58 | GitHubRepoName: !Ref GitHubRepoName 59 | GitHubRepoBranch: !Ref GitHubRepoBranch 60 | GitHubUser: !Ref GitHubUser 61 | GitHubToken: !Ref GitHubToken 62 | CodePipelineRole: !GetAtt ServerlessRoles.Outputs.CodePipelineRole 63 | CloudformationRole: !GetAtt ServerlessRoles.Outputs.CloudformationDeployRole 64 | CodeBuildRole: !GetAtt ServerlessRoles.Outputs.CodeBuildRole 65 | TemplateURL: "https://awscomputeblogimages.s3-us-west-2.amazonaws.com/samfarm-pipeline.yaml" 66 | -------------------------------------------------------------------------------- /website/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var height = $(window).height(); 4 | var width = $(window).width(); 5 | 6 | var game = new Phaser.Game(width, height, Phaser.AUTO, 'aws-serverless-samfarm', { 7 | preload: preload, 8 | create: create, 9 | update: update 10 | }); 11 | 12 | var sprites; 13 | var DESIRED_SAM_COUNT = 1; 14 | var CURRENT_SAM_COUNT = 1; 15 | var MAX_SAM_CHANGE = 10; 16 | var LAST_CHANGE_TIME = Date.now(); 17 | var GET_SAM_COUNT_URL = 'https://68eks9w83m.execute-api.us-east-1.amazonaws.com/Prod/sam'; 18 | var TIME_BETWEEN_COUNT_UPDATE_MS = 4000; 19 | 20 | function preload() { 21 | game.load.spritesheet('spinner', 'squirrel.png', 64, 64); 22 | game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL; 23 | } 24 | 25 | function create() { 26 | game.stage.disableVisibilityChange = true; 27 | game.stage.backgroundColor = '#967da7'; 28 | sprites = game.add.physicsGroup(Phaser.Physics.ARCADE); 29 | createSprites(DESIRED_SAM_COUNT); 30 | } 31 | 32 | function createSprites(numberOfSprites) { 33 | for (var i = 0; i < numberOfSprites; i++) { 34 | var initX = game.rnd.integerInRange(100, width - 100); 35 | var initY = game.rnd.integerInRange(32, height - 32); 36 | var velX = game.rnd.integerInRange(-200, 200); 37 | var velY = game.rnd.integerInRange(-200, 200); 38 | 39 | var s = sprites.create(initX, initY, 'spinner'); 40 | s.body.velocity.set(velX, velY); 41 | s.scale.setTo(1.4, 1.4); 42 | } 43 | 44 | sprites.setAll('body.collideWorldBounds', true); 45 | sprites.setAll('body.bounce.x', 1); 46 | sprites.setAll('body.bounce.y', 1); 47 | } 48 | 49 | function update() { 50 | game.physics.arcade.collide(sprites); 51 | 52 | var countToChange = DESIRED_SAM_COUNT - CURRENT_SAM_COUNT; 53 | 54 | samCountUpdate(countToChange); 55 | } 56 | 57 | function samCountUpdate(samCountChange) { 58 | 59 | var currTime = Date.now(); 60 | 61 | // Only update the number of SAMs on the screen every second 62 | if (currTime - LAST_CHANGE_TIME > 1000) { 63 | 64 | // If we are increasing the amount of SAMs on the screen, take the min of the requested change value and 65 | // the max number that can be updated at a time. 66 | if (samCountChange > 0) { 67 | samCountChange = Math.min(samCountChange, MAX_SAM_CHANGE); 68 | 69 | createSprites(samCountChange); 70 | CURRENT_SAM_COUNT += samCountChange; 71 | LAST_CHANGE_TIME = currTime; 72 | 73 | } else if (samCountChange < 0) { 74 | // Else if we are decreasing the amount of SAMs on the screen, take the max of the negative change value and 75 | // the maximum that can be changed at a time 76 | samCountChange = Math.abs(Math.max(samCountChange, -MAX_SAM_CHANGE)); 77 | 78 | // For each of the SAMs that are being removed, kill it's sprite 79 | for (var i = 0; i < samCountChange; i++) { 80 | sprites.children[i].kill(); 81 | } 82 | 83 | // Remove killed SAMs from the array 84 | sprites.children.splice(0, samCountChange); 85 | 86 | CURRENT_SAM_COUNT -= samCountChange; 87 | LAST_CHANGE_TIME = currTime; 88 | } 89 | } 90 | } 91 | 92 | // Every X seconds as defined in the variable TIME_BETWEEN_COUNT_UPDATE_MS, call our API endpoint to tell use how many 93 | // SAMs should be on the screen. 94 | setInterval(() => { 95 | // Make frontend call to lambda 96 | $.get({ 97 | url: GET_SAM_COUNT_URL 98 | }).done(function(data) { 99 | DESIRED_SAM_COUNT = data; 100 | }); 101 | 102 | }, TIME_BETWEEN_COUNT_UPDATE_MS); 103 | -------------------------------------------------------------------------------- /pipeline/pipeline-roles.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: "2010-09-09" 2 | Description: Common resources for application deployed to Lambda 3 | Parameters: 4 | AppName: 5 | Type: String 6 | Description: Name of the application. 7 | MinLength: "1" 8 | MaxLength: "100" 9 | Resources: 10 | CodeBuildTrustRole: 11 | Description: Creating service role in IAM for AWS CodeBuild 12 | Type: AWS::IAM::Role 13 | Properties: 14 | RoleName: !Sub "${AppName}-codebuild-role" 15 | AssumeRolePolicyDocument: 16 | Statement: 17 | - Effect: Allow 18 | Principal: 19 | Service: [codebuild.amazonaws.com] 20 | Action: sts:AssumeRole 21 | Path: / 22 | CodeBuildRolePolicy: 23 | Type: AWS::IAM::Policy 24 | DependsOn: CodeBuildTrustRole 25 | Description: Setting IAM policy for the service role for AWS CodeBuild 26 | Properties: 27 | PolicyName: CodeBuildRolePolicy 28 | PolicyDocument: 29 | Statement: 30 | - Effect: Allow 31 | Action: ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"] 32 | Resource: ["*"] 33 | - Effect: Allow 34 | Resource: ["*"] 35 | Action: ["s3:*"] 36 | - Effect: Allow 37 | Resource: ["*"] 38 | Action: ["kms:GenerateDataKey*", "kms:Encrypt", "kms:Decrypt"] 39 | - Effect: Allow 40 | Resource: ["*"] 41 | Action: ["sns:SendMessage"] 42 | Roles: [!Ref CodeBuildTrustRole] 43 | CloudFormationTrustRole: 44 | Description: Creating service role in IAM for AWS CloudFormation 45 | Type: AWS::IAM::Role 46 | Properties: 47 | RoleName: !Sub "${AppName}-cloudformation-role" 48 | AssumeRolePolicyDocument: 49 | Statement: 50 | - Effect: Allow 51 | Principal: 52 | Service: [cloudformation.amazonaws.com] 53 | Action: sts:AssumeRole 54 | Path: / 55 | CloudFormationRolePolicy: 56 | Type: AWS::IAM::Policy 57 | DependsOn: CloudFormationTrustRole 58 | Description: Setting IAM policy for the service role for AWS CloudFormation 59 | Properties: 60 | PolicyName: CloudFormationRolePolicy 61 | PolicyDocument: 62 | Statement: 63 | - Action: ["s3:GetObject", "s3:GetObjectVersion", "s3:GetBucketVersioning"] 64 | Resource: "*" 65 | Effect: Allow 66 | - Action: ["s3:PutObject"] 67 | Resource: ["arn:aws:s3:::codepipeline*"] 68 | Effect: Allow 69 | - Action: ["lambda:*"] 70 | Resource: !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:*" 71 | Effect: Allow 72 | - Action: ["apigateway:*"] 73 | Resource: !Sub "arn:aws:apigateway:${AWS::Region}::*" 74 | Effect: Allow 75 | - Action: ["iam:GetRole", "iam:CreateRole", "iam:DeleteRole"] 76 | Resource: !Sub "arn:aws:iam::${AWS::AccountId}:role/${AppName}-*" 77 | Effect: Allow 78 | - Action: ["iam:AttachRolePolicy", "iam:DetachRolePolicy"] 79 | Resource: !Sub "arn:aws:iam::${AWS::AccountId}:role/${AppName}-*" 80 | Effect: Allow 81 | - Action: ["iam:PassRole"] 82 | Resource: ["*"] 83 | Effect: Allow 84 | - Action: ["cloudformation:CreateChangeSet"] 85 | Resource: !Sub "arn:aws:cloudformation:${AWS::Region}:aws:transform/Serverless-2016-10-31" 86 | Effect: Allow 87 | Roles: [!Ref CloudFormationTrustRole] 88 | CodePipelineTrustRole: 89 | Description: Creating service role in IAM for AWS CodePipeline 90 | Type: AWS::IAM::Role 91 | Properties: 92 | RoleName: !Sub "${AppName}-codepipeline-role" 93 | AssumeRolePolicyDocument: 94 | Statement: 95 | - Effect: Allow 96 | Principal: 97 | Service: [codepipeline.amazonaws.com] 98 | Action: sts:AssumeRole 99 | Path: / 100 | CodePipelineRolePolicy: 101 | Type: AWS::IAM::Policy 102 | DependsOn: CodePipelineTrustRole 103 | Description: Setting IAM policy for the service role for AWS CodePipeline 104 | Properties: 105 | PolicyName: CodePipelineRolePolicy 106 | PolicyDocument: 107 | Statement: 108 | - Action: ["s3:GetObject", "s3:GetObjectVersion", "s3:GetBucketVersioning"] 109 | Resource: "*" 110 | Effect: Allow 111 | - Action: ["s3:PutObject"] 112 | Resource: ["arn:aws:s3:::codepipeline*"] 113 | Effect: Allow 114 | - Action: ["codebuild:StartBuild", "codebuild:BatchGetBuilds"] 115 | Resource: "*" 116 | Effect: Allow 117 | - Action: ["cloudwatch:*", "s3:*", "sns:*", "cloudformation:*", "rds:*", "sqs:*", "iam:PassRole"] 118 | Resource: "*" 119 | Effect: Allow 120 | - Action: ["lambda:InvokeFunction", "lambda:ListFunctions"] 121 | Resource: "*" 122 | Effect: Allow 123 | Roles: [!Ref CodePipelineTrustRole] 124 | Outputs: 125 | CodePipelineRole: 126 | Description: "Role for code pipeline" 127 | Value: !GetAtt CodePipelineTrustRole.Arn 128 | CloudformationDeployRole: 129 | Description: "Role for cloudformation to deploy with." 130 | Value: !GetAtt CloudFormationTrustRole.Arn 131 | CodeBuildRole: 132 | Description: "Role for code build to build" 133 | Value: !GetAtt CodeBuildTrustRole.Arn 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aws-serverless-samfarm 2 | This repo contains the full code and templates needed to create a the Serverless SamFarm demo as shown in the What's New with AWS Lambda presentation at Re:Invent 2016. 3 | 4 | There are three separate parts to this application: the api, the pipeline which detects, builds, and deploys changes, and the website. 5 | Lets start by getting that website up and running. 6 | 7 | ## Step 1 8 | 9 | ### Website 10 | In the [website directory](website/) there are four files: 11 | 12 | 1. **[index.html](website/index.html):** This is the index file that our S3 bucket will be displaying. 13 | 2. **[app.js](website/app.js):** The heart of our website. We will be making some changes to this in a bit, but for now we can leave it alone. 14 | 3. **[squirrel.png](website/squirrel.png):** Our friend! SAM the squirrel. 15 | 4. **[website.yaml](website/website.yaml):** The CloudFormation template used to create the Amazon S3 bucket for the website. 16 | 17 | To create the website stack. 18 | 19 | [](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=myteststack&templateURL=https://awscomputeblogimages.s3-us-west-2.amazonaws.com/samfarm-website.yaml) 20 | 21 | Once the stack is complete, we will need to keep track of the S3 bucket name and the URL for the website. 22 | 23 | Now we have all the seperate parts of our website, but lets get SAM up and running: 24 | 25 | ```bash 26 | sh upload_website.sh 27 | ``` 28 | 29 | And visit the url you saved before. You should see something like this: 30 | 31 | ![SAM Screenshot](/img/sam-screenshot.png) 32 | 33 | Ta-da, a working website! SAM the squirrel may be all alone right now, but we'll fix that in a bit. 34 | 35 | 36 | ## Step 2 37 | ### API 38 | The Serverless API we are building! The [api directory](api/) contains five files. 39 | 40 | 1. **[beta.json](api/beta.json):** The CloudFormation staging file. This will be used by CloudFormation to pass parameters to our CloudFormation template. 41 | 2. **[buildspec.yml](api/buildspec.yml):** This is used by CodeBuild in the build step of our pipeline. We will get to that later. 42 | 3. **[index.js](api/index.js):** The Lambda function code! 43 | 4. **[package.json](api/package.json):** The package.json that defines what packages we need for our Lambda function. 44 | 5. **[saml.yaml](api/saml.yaml):** SAML my YAML! This is the SAM template file that will be used to create our API gateway resource and Lambda function, hook them up together 45 | 46 | Create a new github repo from the [api directory](api/) and place these files in there. This repo will be used for your automated CI/CD pipeline we build below. 47 | 48 | ## Step 3 49 | 50 | ### Pipeline 51 | The pipeline is a full CI/CD serverless pipeline for building and deploying your api. In this example we are using CloudFormation to create the pipeline, all resources, and any permissions needed. 52 | 53 | The following resources are created: 54 | 55 | - An S3 bucket to store deployment artifacts. 56 | - An AWS CodeBuild stage to build any changes checked into the repo. 57 | - The AWS CodePipeline that will watch for changes on your repo, and push these changes through to build and deployment steps. 58 | - All IAM roles and policies required. 59 | 60 | The CloudFormation templates being used to create these resources can be found in [pipeline directory](pipeline/). 61 | 62 | To create the pipeline stack, click the launch stack button below. 63 | 64 | [](https://console.aws.amazon.com/cloudformation/home?region=us-east-1#/stacks/new?stackName=myteststack&templateURL=https://awscomputeblogimages.s3-us-west-2.amazonaws.com/samfarm-main.yaml) 65 | 66 | ## Step 4 67 | ### Update your website 68 | If you have looked at the code in the website (or if you haven't, now is your chance!), you may have noticed that the website makes http requests to an api periodically to update the number of SAMs on the screen. Right now it is pointing at nothing, so lets update it to point it to brand spanking new API. In the [app.js](website/app.js), look for the line 69 | 70 | ```javascript 71 | var GET_SAM_COUNT_URL = 'INSERT API GATEWAY URL HERE'; 72 | ``` 73 | 74 | and update that to your API Gateway endpoint. It should be in the format: 75 | 76 | ``` 77 | https://.execute-api.us-east-1.amazonaws.com/Prod/sam 78 | ``` 79 | 80 | Now lets update your S3 static website with this change: 81 | 82 | ```bash 83 | sh upload_website.sh 84 | ``` 85 | 86 | 87 | ## Step 5 88 | ### Start the party (or how I learned to stop worrying and push a change) 89 | Now that we have our website, our code repository with our lambda function and our pipeline configured, lets see it in action. We are going to make two changes to our repository, first were going to setup our API for CORS, and second were going to update our Lambda function. Both changes will be made in the repo you created in Step 2. 90 | 91 | 92 | #### CORS 93 | Go to the beta.json file and update the following line: 94 | 95 | ```json 96 | "OriginUrl": "*" 97 | ``` 98 | 99 | to the url for the S3 static site we created. Something like: 100 | 101 | ```json 102 | "OriginUrl": "http://.s3-website-us-east-1.amazonaws.com" 103 | ``` 104 | 105 | 106 | #### Lambda Function 107 | Go to index.js file the repo you made in Step 2 and update the line: 108 | 109 | ```javascript 110 | var samCount = 1; 111 | ``` 112 | 113 | to 114 | 115 | ```javascript 116 | var samCount = 15; 117 | ``` 118 | 119 | Commit and push the changes. 120 | 121 | Go back to the pipeline we generated in Step 3, you will see AWS CodePipeline automatically pick up your change, and start the build and deploy process. Voila! A completely version controlled, serverless, CI/CD solution to give a squirrel a few friends. Technology! 122 | -------------------------------------------------------------------------------- /pipeline/pipeline.yaml: -------------------------------------------------------------------------------- 1 | AWSTemplateFormatVersion: "2010-09-09" 2 | Description: "Template for full CI/CD serverless applications." 3 | Parameters: 4 | AppName: 5 | Type: String 6 | Description: Name of the application. 7 | MinLength: "1" 8 | MaxLength: "80" 9 | AllowedPattern: "[A-Za-z0-9-]+" 10 | ConstraintDescription: Malformed input parameter. AppName must only contain upper and lower case letters, numbers, and -. 11 | SAMInputFile: 12 | Type: String 13 | Description: The filename for the SAM file. 14 | Default: saml.yaml 15 | SAMOutputFile: 16 | Type: String 17 | Description: The filename for the output SAM file from the buildspec file. 18 | Default: post-saml.yaml 19 | StagingFile: 20 | Type: String 21 | Description: The cloudformation staging file. Leave empty if no staging file is needed. 22 | Default: beta.json 23 | CodeBuildImage: 24 | Type: String 25 | Default: "aws/codebuild/nodejs:7.0.0" 26 | Description: Image used for CodeBuild project. 27 | GitHubRepoName: 28 | Type: String 29 | Description: The GitHub repo name 30 | GitHubRepoBranch: 31 | Type: String 32 | Description: The GitHub repo branch code pipelines should watch for changes on 33 | Default: master 34 | GitHubUser: 35 | Type: String 36 | Description: GitHub UserName. This username must have access to the GitHubToken. 37 | GitHubToken: 38 | NoEcho: true 39 | Type: String 40 | Description: "Secret. OAuthToken with access to Repo. Long string of characters and digits. Go to https://github.com/settings/tokens" 41 | CodePipelineRole: 42 | Type: String 43 | Description: Role the pipeline will use 44 | CloudformationRole: 45 | Type: String 46 | Description: Role for cloudformation 47 | CodeBuildRole: 48 | Type: String 49 | Description: Role for code build 50 | Conditions: 51 | HasStagingVariables: 52 | !Not 53 | - !Equals [!Ref StagingFile, ""] 54 | Resources: 55 | CodeBuildProject: 56 | DependsOn: [S3Bucket] 57 | Description: Creating AWS CodeBuild project 58 | Type: AWS::CodeBuild::Project 59 | Properties: 60 | Artifacts: 61 | Type: CODEPIPELINE 62 | Description: !Sub "Building stage for ${AppName}." 63 | Environment: 64 | ComputeType: BUILD_GENERAL1_SMALL 65 | EnvironmentVariables: 66 | - Name: S3_BUCKET 67 | Value: !Ref S3Bucket 68 | Image: !Ref CodeBuildImage 69 | Type: LINUX_CONTAINER 70 | Name: !Sub "${AppName}-build" 71 | ServiceRole: !Ref CodeBuildRole 72 | Source: 73 | Type: CODEPIPELINE 74 | Tags: 75 | - Key: app-name 76 | Value: !Ref AppName 77 | TimeoutInMinutes: 5 78 | S3Bucket: 79 | Description: Creating Amazon S3 bucket for AWS CodePipeline artifacts 80 | Type: AWS::S3::Bucket 81 | DeletionPolicy: Retain 82 | Properties: 83 | BucketName: !Sub "serverless-app-${AWS::AccountId}-${AWS::Region}-${AppName}" 84 | VersioningConfiguration: 85 | Status: Enabled 86 | S3ArtifactBucketPolicy: 87 | DependsOn: [S3Bucket] 88 | Description: Setting Amazon S3 bucket policy for AWS CodePipeline access 89 | Type: AWS::S3::BucketPolicy 90 | Properties: 91 | Bucket: !Ref S3Bucket 92 | PolicyDocument: 93 | Version: "2012-10-17" 94 | Id: SSEAndSSLPolicy 95 | Statement: 96 | - Sid: DenyInsecureConnections 97 | Effect: Deny 98 | Principal: "*" 99 | Action: s3:* 100 | Resource: !Sub "arn:aws:s3:::${S3Bucket}/*" 101 | Condition: 102 | Bool: 103 | aws:SecureTransport: false 104 | ProjectPipeline: 105 | DependsOn: [S3Bucket, CodeBuildProject] 106 | Description: Creating a deployment pipeline for your project in AWS CodePipeline 107 | Type: AWS::CodePipeline::Pipeline 108 | Properties: 109 | Name: !Sub "${AppName}-pipeline" 110 | RoleArn: !Ref CodePipelineRole 111 | Stages: 112 | - Name: Source 113 | Actions: 114 | - Name: source 115 | InputArtifacts: [] 116 | ActionTypeId: 117 | Version: "1" 118 | Category: Source 119 | Owner: ThirdParty 120 | Provider: GitHub 121 | OutputArtifacts: 122 | - Name: !Sub "${AppName}-SourceArtifact" 123 | Configuration: 124 | Repo: !Ref GitHubRepoName 125 | Branch: !Ref GitHubRepoBranch 126 | OAuthToken: !Ref GitHubToken 127 | Owner: !Ref GitHubUser 128 | RunOrder: 1 129 | - Name: Build 130 | Actions: 131 | - Name: build-from-source 132 | InputArtifacts: 133 | - Name: !Sub "${AppName}-SourceArtifact" 134 | ActionTypeId: 135 | Category: Build 136 | Owner: AWS 137 | Version: "1" 138 | Provider: CodeBuild 139 | OutputArtifacts: 140 | - Name: !Sub "${AppName}-BuildArtifact" 141 | Configuration: 142 | ProjectName: !Sub "${AppName}-build" 143 | RunOrder: 1 144 | - Name: Deploy 145 | Actions: 146 | - Name: create-changeset 147 | InputArtifacts: 148 | - Name: !Sub "${AppName}-BuildArtifact" 149 | ActionTypeId: 150 | Category: Deploy 151 | Owner: AWS 152 | Version: "1" 153 | Provider: CloudFormation 154 | OutputArtifacts: [] 155 | Configuration: 156 | StackName: !Sub "${AppName}-serverless-stack" 157 | ActionMode: CHANGE_SET_REPLACE 158 | RoleArn: !Ref CloudformationRole 159 | ChangeSetName: pipeline-changeset 160 | Capabilities: CAPABILITY_NAMED_IAM 161 | TemplatePath: !Sub "${AppName}-BuildArtifact::${SAMOutputFile}" 162 | TemplateConfiguration: !If [ HasStagingVariables, !Sub "${AppName}-BuildArtifact::${StagingFile}", "" ] 163 | RunOrder: 1 164 | - Name: execute-changeset 165 | InputArtifacts: [] 166 | ActionTypeId: 167 | Category: Deploy 168 | Owner: AWS 169 | Version: "1" 170 | Provider: CloudFormation 171 | OutputArtifacts: [] 172 | Configuration: 173 | StackName: !Sub "${AppName}-serverless-stack" 174 | ActionMode: CHANGE_SET_EXECUTE 175 | ChangeSetName: pipeline-changeset 176 | RunOrder: 2 177 | ArtifactStore: 178 | Type: S3 179 | Location: !Ref S3Bucket 180 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------