Know How Guide and Hands on Guide for AWS
Fork the github repository: https://github.com/nikosheng/sam-cicd and clone your repository to your environment such as Cloud9
Install AWS CLI, Python 3 & Pip 3, Docker and setup the python virtualenv
Install SAM CLI
Build Succeeded
Built Artifacts : .aws-sam/build Built Template : .aws-sam/build/template.yaml
1. Invoking function locally using a local sample payload
```bash
sam local invoke HelloWorldFunction --event event.json
Invoking app.lambda_handler (python3.7)
Fetching lambci/lambda:python3.7 Docker container image.................................................................................
Mounting /home/ec2-user/workspace/sam-cicd/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container
START RequestId: d27c75d0-1218-1bca-b08d-0100d39feb2d Version: $LATEST
END RequestId: d27c75d0-1218-1bca-b08d-0100d39feb2d
REPORT RequestId: d27c75d0-1218-1bca-b08d-0100d39feb2d Init Duration: 319.41 ms Duration: 459.81 ms Billed Duration: 500 ms Memory Size: 128 MB Max Memory Used: 30 MB
{"statusCode":200,"body":"{\"message\": \"hello world! v1.4\", \"location\": \"68.79.5.140\"}"}
Mounting HelloWorldFunction at http://127.0.0.1:3000/hello [GET] You can now browse to the above endpoints to invoke your functions. You do not need to restart/reload SAM CLI while working on your functions, changes will be reflected instantly/automatically. You only need to restart SAM CLI if you update your AWS SAM template 2020-10-20 09:18:37 * Running on http://127.0.0.1:3000/ (Press CTRL+C to quit)
- Invoke local API Gateway
```bash
curl http://127.0.0.1:3000/hello
{"message": "hello world! v1.4", "location": "68.79.5.140"}
Fetching lambci/lambda:python3.7 Docker container image…… Mounting /home/ec2-user/workspace/sam-cicd/.aws-sam/build/HelloWorldFunction as /var/task:ro,delegated inside runtime container START RequestId: 5039dc9e-eba4-1ab4-a641-7c48576d91ef Version: $LATEST END RequestId: 5039dc9e-eba4-1ab4-a641-7c48576d91ef REPORT RequestId: 5039dc9e-eba4-1ab4-a641-7c48576d91ef Init Duration: 396.17 ms Duration: 481.82 ms Billed Duration: 500 ms Memory Size: 128 MB Max Memory Used: 30 MB No Content-Type given. Defaulting to ‘application/json’. 2020-10-20 09:23:55 127.0.0.1 - - [20/Oct/2020 09:23:55] “GET /hello HTTP/1.1” 200 -
## Packaging and deployment via SAM CLI
1. Package
```bash
cd .aws-sam/build/
sam package --template-file template.yaml \
--output-template-file packaged.yaml \
--s3-bucket serverless-hands-on --s3-prefix sam-app-demo
Successfully created/updated stack - sam-app-demo in cn-north-1
3. Retrieve the API Gateway Endpoint URL:
```bash
aws cloudformation describe-stacks \
--stack-name sam-app-demo \
--query 'Stacks[].Outputs[?OutputKey==`HelloWorldApi`].OutputValue' \
--output table --region cn-north-1
curl <HelloWorldApi_RUL>
sam logs -n HelloWorldFunction --stack-name sam-app-demo --tail --region cn-north-1
cd sam-cicd
pip install pytest pytest-mock --user
python -m pytest tests/ -v
tests/unit/test_handler.py::test_lambda_handler PASSED [100%]
aws cloudformation delete-stack --stack-name sam-app-demo --region cn-north-1
Due to AWS CodePipeline has not been launched in China region, the below lab are running on us-east-1. If you want to run the lab on China region, please check the Lab CICD-Jenkins
AWS CloudFormationAWSLambdaExecutecfn-lambda-pipelinecfn-lambda-pipeline-inline
{
"Statement": [
{
"Action": [
"apigateway:*",
"codedeploy:*",
"lambda:*",
"cloudformation:CreateChangeSet",
"iam:GetRole",
"iam:CreateRole",
"iam:DeleteRole",
"iam:PutRolePolicy",
"iam:AttachRolePolicy",
"iam:DeleteRolePolicy",
"iam:DetachRolePolicy",
"iam:PassRole",
"s3:GetObjectVersion",
"s3:GetBucketVersioning"
],
"Resource": "*",
"Effect": "Allow"
}
],
"Version": "2012-10-17"
}
Create a pipeline to deploy the application. The pipeline monitors Github repository changes, runs AWS CodeBuild builds to create deployment packages, and uses AWS CloudFormation to deploy applications. In the process of creating a pipeline, you can also create an AWS CodeBuild build project.
aftership-lambda-pipelineNew service Role(aftership-lambda-pipeline-role)Allow AWS CodePipeline to create a service role so it can be used with this new pipelineAdvanced settings - Artifact Store – Default locationGithub (version1)Connect to Githubsam-cicdmasterGithub WebhookAWS CodeBuildcreate Projectaftership-codebuildManaged image – Operating system - ubuntu - Runtime(s) - standard - Image - aws/codebuild/standard:4.0 - Image version - Always use the latest image for runtime version - Environment type - LinuxNew service role – codebuild-aftership-lambda-pipeline-service-roleaftership-serverless-demo-bucketus-east-1
- Buildspec – Build specifications – Use a buildspec file
- Logs – CloudWatch logs - optional - /aws/codebuild/aftership-serverless as logs group name
- Continue to CodePipeline
- Build type - Single Buildcreate or update a stackaftership-lambda-pipeline-stackBuildArtifact::packaged.yamlUse configuration file - BuildArtifact::cfn-template-config.jsonCAPABILITY_IAM & CAPABILITY_AUTO_EXPANDcfn-lambda-pipelineIt will failed on Build stage due to permission issue
codebuild-aftership-lambda-pipeline-service-role IAM Role
AmazonS3FullAccess policyRelease change to trigger the Pipeline again
If you find the Deploy stage failed with AWS CloudFormation error, you can continue, we will confiure the CodeDeploy for Lambda canary deployment
No deployment configuration found for name: Canary20Percent3Minutes
Open the CodeDeploy -> Deployment configurations
Canary20Percent3MinutesAWS LambdaCanary20Canary20Percent3Minutes DeploymentPreference:
Type: String
Default: AllAtOnce
AllowedValues:
- Canary20Percent3Minutes
- Canary50Percent1Minutes
- Canary50Percent5Minutes
- Canary10Percent5Minutes
- Canary10Percent10Minutes
- Canary10Percent15Minutes
- Canary10Percent30Minutes
- Linear10PercentEvery1Minute
- Linear10PercentEvery2Minute
- Linear10PercentEvery3Minute
- Linear10PercentEvery10Minute
- AllAtOnce
cfn-template-config.json
"DeploymentPreference": "Canary20Percent3Minutes"
from datetime import datetime
now = datetime.now()
dt_string = now.strftime("%Y-%m-%d-%H-%M-%S")
return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world! canary " + dt_string,
"location": ip.text.replace("\n", "")
}),
}
git status
git add .
git commit -m "update app.py"
git push

Testing
curl https://ac0acgrwej.execute-api.us-east-1.amazonaws.com/staging/hello
{"message": "hello world! canary 2020-10-21-23-37-32", "location": "100.24.118.93"}
Modify the hello_world/app.py code as below
return {
"statusCode": 200,
"body": json.dumps({
"message": "hello world! Latest canary " + dt_string,
"location": ip.text.replace("\n", "")
}),
}
After you submit, you can monitor the deployment

And you can testing the URL, you can find the hello world! Latest canary
{"message": "hello world! canary 2020-10-21-23-42-49", "location": "100.24.118.93"}[ec2-user@ip-10-0-2-83 sam-cicd]$ curl https://ac0acgrwej.execute-api.us-east-1.amazonaws.com/staging/hello
{"message": "hello world! canary 2020-10-21-23-42-51", "location": "100.24.118.93"}[ec2-user@ip-10-0-2-83 sam-cicd]$ curl https://ac0acgrwej.execute-api.us-east-1.amazonaws.com/staging/hello
{"message": "hello world! canary 2020-10-21-23-42-53", "location": "100.24.118.93"}[ec2-user@ip-10-0-2-83 sam-cicd]$ curl https://ac0acgrwej.execute-api.us-east-1.amazonaws.com/staging/hello
{"message": "hello world! Latest canary 2020-10-21-23-42-56", "location": "18.204.205.147"}[ec2-user@ip-10-0-2-83 sam-cicd]$ curl https://ac0acgrwej.execute-api.us-east-1.amazonaws.com/staging/hello

sudo wget -O /etc/yum.repos.d/jenkins.repo https://pkg.jenkins.io/redhat-stable/jenkins.repo sudo rpm –import https://pkg.jenkins.io/redhat-stable/jenkins.io.key sudo yum install jenkins sudo chkconfig jenkins on sudo service jenkins start
sudo yum install git -y sudo yum install docker -y sudo service docker start
4. Check the Jenkins initial password
```bash
sudo cat /var/lib/jenkins/secrets/initialAdminPassword

The scope:
github_hook in Jenkins for GitHub pushes and to grant it Overall read, Job create and Job read.

Github Access Token

wget https://raw.githubusercontent.com/aws-samples/aws-serverless-workshop-greater-china-region/master/Lab8B-CICD-Jenkins/buildspec.yml
wget https://raw.githubusercontent.com/aws-samples/aws-serverless-workshop-greater-china-region/master/Lab8B-CICD-Jenkins/appspec.template.yaml
wget https://raw.githubusercontent.com/aws-samples/aws-serverless-workshop-greater-china-region/master/Lab8B-CICD-Jenkins/lambda_function.py
2. Using the lambda_function.py to create the simple lambda function `jenkins-cicd-helloworld`
3. publish lambda,with version `1` and create the alias `stable_helloworld`
4. Update the appspec.template.yaml and buildspec.yml
## Configure the CodeDeploy
1. Create CodeDeploy application `first-try-with-jenkins`, Computer Platform as `lambda`

2. Create deployment group under application with group name `first-try-with-jenkins`

## Create Jenkins Project
1. Create a new project - `freestyle project` - `AWS-BJS-CodeDeploy-Jenkins` as Name
2. Source Management - Git - `https://github.com/you_account/sam-cicd.git`
3. Trigger - `Github hook trigger for GITScm polling`
4. Add the execution Shell and update based on the buildspec.yml content
```bash
ls
mkdir -p jenkins_build
CurrentVersion=$(echo $(aws lambda get-alias --function-name arn:aws-cn:lambda:cn-north-1:account_id:function:jenkins-cicd-helloworld --name stable_helloworld --region cn-north-1 | grep FunctionVersion | tail -1 |tr -cd "[0-9]"))
zip -r ./jenkins_build/lambda.zip ./jenkins_hello_world/lambda_function.py
aws lambda update-function-code --function-name arn:aws-cn:lambda:cn-north-1:account_id:function:jenkins-cicd-helloworld --zip-file fileb://jenkins_build/lambda.zip --region cn-north-1 --publish
TargetVersion=$(echo $(aws lambda list-versions-by-function --function-name arn:aws-cn:lambda:cn-north-1:account_id:function:jenkins-cicd-helloworld --region cn-north-1 | grep Version | tail -1 | tr -cd "[0-9]"))
echo $CurrentVersion
echo $TargetVersion
sed -e 's//'$CurrentVersion'/g' -e 's//'$TargetVersion'/g' ./jenkins_hello_world/appspec.template.yaml > appspec.yaml
aws s3 cp appspec.yaml s3://serverless-hands-on/jenkins_build/helloworld/codedeploy/appspec.yaml --region cn-north-1
rm ./jenkins_hello_world/appspec.template.yaml
cat appspec.yaml
aws deploy create-deployment --region cn-north-1 --application-name first-try-with-jenkins --deployment-group-name first-try-with-jenkins --s3-location bucket='serverless-hands-on',key='jenkins_build/helloworld/codedeploy/appspec.yaml',bundleType=YAML


first-try-with-jenkins

Environment

buildspec

Update the codebuild-first-try-with-jenkins-service-role by adding the
serverless-hands-oncodedeploy:CreateDeployment permission



first-try-with-jenkins

Replace the execution Shell by CodeBuild. Here we will use the sam-cicd/jenkins_hello_world/buildspec.yml
Testing


