aws-blueprint example for an API backed by a single lambda
Each branch of this repo is a different language for the same example app.
This example sets up a CI/CD for a single lambda, fronted by CloudFront and API Gateway.
- Resources CloudFormation: aws/cloudformation/cf-apig-single-lambda-resources.yaml
- CI/CD CloudFormation: single-lambda-test-staging-prod.yaml
-
Run the setup script from your local git repo dir:
wget -q https://raw.githubusercontent.com/rynop/abp-single-lambda-api/master/bin/setup.sh; bash setup.sh; rm setup.shThis:
- Downloads the branch of of your favorite programming language, and common aws dir out of
masterbranch. - Sets
NestedStacksS3Bucketand s3 versions of yournested-stacksin your resources CloudFormation file.
- Downloads the branch of of your favorite programming language, and common aws dir out of
-
Create a s3 bucket that will hold your lambda zips. Only need one bucket per AWS. Ex:
deploy.yourdomain.com -
Create your "resources" (CloudFront, API Gateway etc) stacks using
aws/cloudformation/cf-apig-single-lambda-resources.yamlin your repo. Stack naming convention is[stage]--[repo]--[branch]--[eyecatcher]--r. Ex:prod--abp-single-lambda-api--master--ImageManip--r:- Create a stack for your
testandprodstages. You will have 2 root stacks. Theprodstack takes care of bothprodandstagingresources. - The
Outputstab in the CloudFormation UI for each root stack has commands you will run in the next steps. Outputs that start withRun*you should run from your CLI.
- Create a stack for your
-
Some of the Lambda configuration is stored in Systems manager parameter store with the convention based prefix
/<stage>/<repoName>/<branch>/<lambdaName>/. Ex:aws ssm put-parameter --name "/prod/abp-single-lambda-api/master/ResizeImage/lambdaExecutionRoleArn" --type "String" --value 'arn:aws:iam::accountId:role/roleName'. These keys are required per stage:/<stage>/<repoName>/<branch>/<lambdaName>/lambdaExecutionRoleArn(don't create forstagingstage.staginguses theprodlambdaExecutionRoleArn). The output of the Resources CloudFormation stack contains aSsmSetLambdaExecutionRoleCmdvalue that is an aws CLI command that sets this for you./<stage>/<repoName>/<branch>/<lambdaName>/lambdaTimeout/<stage>/<repoName>/<branch>/<lambdaName>/lambdaMemory/<stage>/<repoName>/<branch>/<lambdaName>/vpcConfig. Optional. If your lambda needs to be in vpc, value should be likeSubnetIds=string,string,SecurityGroupIds=string,string. See AWS CLI docs for more info.
-
Setup env vars per stage. All keys in the
lambdaEnvsnamespace are automatically added your your lambda's env. They can optionally be encrypted in systems manager param store, we handle all the decoding complexity (if you use the default KMS key). You can use this script help set them./<stage>/<repoName>/<branch>/<lambdaName>/lambdaEnvs/<env var name>. Ex:/prod/abp-single-lambda-api/master/ResizeImage/lambdaEnvs/MY_VAR- Run the
SsmSetXFromCdnEnvVarCmdoutput value from the Resources CloudFormation stack. It setsX_FROM_CDN(used by the example code in this repo). - These env vars get set for your in the lambda configuration:
APP_STAGE
-
Create a Github user (acct will just be used to read repos for CI/CD), give it read auth to your github repo. Create a personal access token for this user at https://github.com/settings/tokens. This token will be used by the CI/CD to pull code.
-
Go through the
README.mdof the language branch you copied, language specific setup and for stack parameter values that will be used in CI/CD stack creation (next step). -
Create a CloudFormation stack for your CI/CD using single-lambda-test-staging-prod.yaml with the stack naming convention of
[repo]--[branch]--[eyecatcher]--cicd. Ex:abp-single-lambda-api--master--ResizeImage--cicd. -
Commit your code and the CI/CD CodePipline will automatically run. The buildspec files assume you check in your dependencies (aka
node_modules). Prevents version issues when working in teams, also prevents deploy issues during github outage. -
The domain your app can be reached, is located in the
Outputstab of the resources CloudFormation stack at keyCNAME. -
Create a DNS entry in route53 for production that consumers will use. The cloud formation creates one for
prod--but you do not want to use this as the CloudFormation can be deleted.
Want a lambda that does not need a web API (that is invoked by something like sns)? Follow the steps above, but instead of using aws/cloudformation/cf-apig-single-lambda-resources.yaml for your resources CloudFormation, use aws/cloudformation/no-web-api-single-lambda-resources.yaml. You will use the same codebuild files and same CI/CD.
Astute developers may notice an APIG line in aws/codebuild/lambda-publish, however this has no impact and will not fail the build.
The publishing process is multi-stage, with manual approvals, all handled in an automated fashion. Here are the details:
PublishTeststep:- Create Lambda if DNE. Set env vars from ssm namespace
/test/[repo]/[branch]/[LAMBDA_NAME]/lambdaEnvs, role from/test/[repo]/[branch]/[LAMBDA_NAME]/lambdaExecutionRoleArn, timeout from/test/[repo]/[branch]/[LAMBDA_NAME]/lambdaTimeout, memory from/test/[repo]/[branch]/[LAMBDA_NAME]/lambdaMemory, - Create Lambda version & alias
test
- Create Lambda if DNE. Set env vars from ssm namespace
- When CodePipeline
ApproveTestapproved,PublishStagingstep: :- Update lambda. Set env vars from ssm namespace
/staging/[repo]/[branch]/[LAMBDA_NAME]/lambdaEnvs, role from/prod/[repo]/[branch]/[LAMBDA_NAME]/lambdaExecutionRoleArn, timeout from/staging/[repo]/[branch]/[LAMBDA_NAME]/lambdaTimeout, memory from/test/[repo]/[branch]/[LAMBDA_NAME]/lambdaMemory. - Copy zip package to
s3://${S3_BUCKET_CONTAINING_PACKAGES}/${S3_PATH_TO_PACKAGES}/${codeSha256}.zipwherecodeSha256is the just deployedstaginglambdaCodeSha256configuration value. This ensures the code deployed to staging is the code that will be deployed toprod. - Create Lambda version & alias
staging
- Update lambda. Set env vars from ssm namespace
- When CodePipeline
ApproveStagingapproved,PublishProdstep: :- Update lambda using
${codeSha256}.zip. Set env vars from ssm namespace/prod/[repo]/[branch]/[LAMBDA_NAME]/lambdaEnvs, role from/prod/[repo]/[branch]/[LAMBDA_NAME]/lambdaExecutionRoleArn, timeout from/staging/[repo]/[branch]/[LAMBDA_NAME]/lambdaTimeout, memory from/prod/[repo]/[branch]/[LAMBDA_NAME]/lambdaMemory. - Create Lambda version & alias
prod
- Update lambda using