This workshop consists of multiple levels of increasing difficulty. The basic track of this course uses the AWS Web Console. However, if you prefer working with the AWS CLI or with Terraform you may switch to one of these technologies at any stage in the course. If you stay on the basic track, there is nothing to install on your machine. If you want to switch to the CLI or to Terraform, you need to follow the optional installation instructions below.
At the start of the course, please take care of the following tasks:
You have received an AWS Account ID, an IAM user name and a password from the trainers. Please navigate to https://console.aws.amazon.com/, choose "IAM user", and enter the Account ID and then your credentials. This logs you into the console. From there you should be able to reach the service Lambda.
If you want to also work on some of the optional extra topics of this course, you need to install the AWS CLI on your machine. Please follow the AWS CLI installation instructions and choose the installation method best suited for your operating system.
In order to authenticate your CLI you need to first create an access key by performing the following steps:
- Log in to the AWS Console with your credentials
- Click on your name in the top right of the screen
- Click
My Security Credentialsin the dropdown - Click
Create Access KeyunderAccess keys for CLI, SDK, & API access
Then you need to configure your CLI with access key ID and the secret access key
- Type
aws configurein your terminal - Copy your access key ID and the secret access key from the web console and paste them at the prompts
- Choose
eu-central-1as the default region name - Test the connection by typing
aws sts get-caller-identityin your terminal. You should see some basic information about your user.
Some optional extra topics also require Terraform, which you need to install on your machine. Please follow the Terraform installation instructions and choose the installation method best suited for your operating system.
In this level you will learn how to create a first simple function in AWS Lambda. You will also learn how to pass configuration parameters into a function using environment variables. Furthermore, you will see that AWS has a very strict, deny-by-default permission scheme.
Please work through the following steps:
- Go to the AWS Lambda GUI
- Click on
Create function - Choose
my-function-AWSUSERas the function name, replacingAWSUSERwith you user name - Choose
Node.js 14.xas the runtime - Open the section
Change default execution roleand note that the UI automatically creates an execution role behind the scenes, granting the function certain privileges - Click
Create function - Copy the code from ./level-0/function/index.js and paste it into the code editor field
- Press the
Deploybutton - Set environment variable
NAMEin theConfigurationtab underEnvironment variables - Press the
Testbutton and create a test event calledtest - Press the
Testbutton again to run the test - Observe the test output
Try it with the AWS CLI!
- Set the AWSUSER environment variable.
export AWSUSER=<your AWS username>
- Create an execution role which will allow Lambda functions to access AWS resources:
aws iam create-role --role-name lambda-exec-cli-"$AWSUSER" --assume-role-policy-document '{"Version": "2012-10-17","Statement": [{ "Effect": "Allow", "Principal": {"Service": "lambda.amazonaws.com"}, "Action": "sts:AssumeRole"}]}'
- Grant certain permissions to your newly created role. The managed policy
AWSLambdaBasicExecutionRolehas the permissions needed to write logs to CloudWatch:
aws iam attach-role-policy --role-name lambda-exec-cli-"$AWSUSER" --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
- Create a deployment package for your function:
zip -j function.zip level-0/function/index.js
- Create the function:
Find out your Account ID by clicking your username in the top right corner.
export ACCOUNT_ID=<your account ID>
aws lambda create-function --function-name my-function-cli-"$AWSUSER" --zip-file fileb://function.zip --handler index.handler --runtime nodejs14.x --role arn:aws:iam::"$ACCOUNT_ID":role/lambda-exec-cli-"$AWSUSER"
- Set the
NAMEenvironment variable to your user name:
aws lambda update-function-configuration --function-name my-function-cli-"$AWSUSER" --environment "Variables={NAME='$AWSUSER'}"
- Invoke the function:
aws lambda invoke --function-name my-function-cli-"$AWSUSER" out --log-type Tail
- Invoke the function and decode the logs:
aws lambda invoke --function-name my-function-cli-"$AWSUSER" out --log-type Tail --query 'LogResult' --output text | base64 -d
Still bored? Then try it with Terraform!
- Copy the Terraform module to a directory of your choice
export WORKDIR=<your work directory>
mkdir $WORKDIR
cp level-0/advanced/terraform/* $WORKDIR
cp -r level-0/function $WORKDIR
- Navigate to the Terraform module in your work directory
cd $WORKDIR
- Initialize the Terraform module
terraform init
- Set your AWS user name as a environment variable for Terraform
export TF_VAR_aws_user=<your AWS user name>
- Apply the Terraform module
terraform apply
- Invoke the function
aws lambda invoke --function-name=$(terraform output -raw function_name) response.json
To reach level 1, you'll need to learn about the following topics:
- Logging
- Event parameters
- Permissions
- Go to the AWS Lambda UI
- Click on
Functionsin the left navigation - Choose the function
my-function-AWSUSER, which you created in level 0 - Copy the code from ./level-1/function/index.js and paste it over the existing code in the editor field
- Press the
Deploybutton - Press the
Testbutton and create a test event calledbob - Paste the following JSON object to the editor field
{
"name": "Bob"
}
- Press the
Testbutton again to run the test - Navigate to the tab
Monitor - Click
View logs in CloudWatch - Look for a recent log stream and open it
- Check for lines looking like this
2021-09-10T12:26:33.779Z c70ee5e7-4295-4408-a713-9f3ceaaa53e3 INFO Bob invoked me
2021-09-10T12:26:33.779Z c70ee5e7-4295-4408-a713-9f3ceaaa53e3 ERROR Oh noes!
- Navigate to the tab
Configurationand click on the categoryPermissions. - Observe the logging permissions which were assigned to your function automatically.
Try it with the AWS CLI!
- Make sure the AWSUSER and ACCOUNT_ID environment variables are still set.
export AWSUSER=<your AWS username>
export ACCOUNT_ID=<your account ID>
- Create a deployment package for your new function:
zip -j function.zip level-1/function/index.js
- Update the function with the new code:
aws lambda update-function-code --function-name my-function-cli-"$AWSUSER" --zip-file fileb://function.zip
- Invoke the function with a test event:
aws lambda invoke --function-name my-function-cli-"$AWSUSER" --cli-binary-format raw-in-base64-out --payload '{ "name": "Bob" }' out --log-type Tail
- Find the latest log stream for your function in CloudWatch:
aws logs describe-log-streams --log-group-name=/aws/lambda/my-function-cli-"$AWSUSER"
- Inspect the log events of the log stream. You might have to escape some characters in the value passed in
--log-stream-name
aws logs get-log-events --log-group-name=/aws/lambda/my-function-cli-"$AWSUSER" --log-stream-name=<name of latest log stream>
Still bored? Then try it with Terraform!
- Make sure your work directory and user variables are still set
export WORKDIR=<your work directory>
export TF_VAR_aws_user=<your AWS user name>
- Navigate to the Terraform module
cp -r level-1/function $WORKDIR
- Apply the Terraform module again
terraform apply
- Invoke the function with a test event:
aws lambda invoke --function-name my-function-cli-"$AWSUSER" --cli-binary-format raw-in-base64-out --payload '{ "name": "Bob" }' out --log-type Tail
- Find the latest log stream for your function in CloudWatch:
aws logs describe-log-streams --log-group-name=/aws/lambda/my-function-cli-"$AWSUSER"
- Inspect the log events of the log stream:
aws logs get-log-events --log-group-name=/aws/lambda/my-function-cli-"$AWSUSER" --log-stream-name=<name of latest log stream>
To reach level 2, you will learn about tracing in your function using AWS XRay. Additionally, we will use a DynamoDB table, which will allow us to trace calls from the function to the table.
We modify the the function to read a joke from a joke table and change the function parameters to receive a jokeID parameter by which it reads from the database.
- Go to the AWS Lambda UI
- Click on
Functionsin the left navigation - Choose the function
my-function-AWSUSER, which you created in level 0 - Create a zip file from the
functionfolder and upload it to the function - Press the
Deploybutton - Grant DynamoDB permission to function
- Grant XRay permission to function
- In the
Configurationtab of the lambda function, select theMonitoring and operations tools, click edit and enableActive tracingin theAWS X-Raysection. - On the functions
Testtab create a test event with the following payload{ "jokeID": "1" }and click theTestbutton. You should see the joke loaded from the database in the response. - On the
Monitortab, select theTracesmenu option and inspect the service map as well as the individual traces. Click on one of the traces to get familiar of what info you have available, such as how long the request to query the DynamoDB took.
Try it with the AWS CLI!
- Make sure the AWSUSER and ACCOUNT_ID environment variables are still set.
export AWSUSER=<your AWS username>
export ACCOUNT_ID=<your account ID>
- Attach a policy for XRay access to your role
aws iam attach-role-policy --role-name lambda-exec-cli-"$AWSUSER" --policy-arn arn:aws:iam::aws:policy/AWSXrayWriteOnlyAccess
- Create a policy for access to the Jokes table in DynamoDB
aws iam create-policy --policy-name read-jokes-db-table-cli-"$AWSUSER" --policy-document '{ "Version": "2012-10-17", "Statement": [{ "Sid": "ReadWriteTable", "Effect": "Allow", "Action": [ "dynamodb:BatchGetItem", "dynamodb:GetItem", "dynamodb:Query", "dynamodb:Scan" ], "Resource": "arn:aws:dynamodb:eu-central-1:'$ACCOUNT_ID':table/Jokes" }]}'
- Attach the policy to your role
aws iam attach-role-policy --role-name lambda-exec-cli-"$AWSUSER" --policy-arn arn:aws:iam::"$ACCOUNT_ID":policy/read-jokes-db-table-cli-"$AWSUSER"
- Create a deployment package for your new function:
zip -j function.zip level-2/function/index.js
- Update the function with the new code:
aws lambda update-function-code --function-name my-function-cli-"$AWSUSER" --zip-file fileb://function.zip
- Invoke the function with a test event:
aws lambda invoke --function-name my-function-cli-"$AWSUSER" out --cli-binary-format raw-in-base64-out --payload '{ "jokeID": "1" }' --log-type Tail --query 'LogResult' --output text | base64 -d
- Switch on XRay tracing for your function
aws lambda update-function-configuration --function-name my-function-cli-"$AWSUSER" --tracing-config "Mode=Active"
- Inspect the traces that have been created during the last 20 minutes:
aws xray get-service-graph --start-time $(($(date +"%s") -1200)) --end-time $(date +"%s")
Still bored? Then try it with Terraform!
- Make sure your work directory and user variables are still set
export WORKDIR=<your work directory>
export TF_VAR_aws_user=<your AWS user name>
- Copy the new function code and the updated terraform resources to your work directory
cp level-2/advanced/terraform/* $WORKDIR
cp -r level-2/function $WORKDIR
- Apply the Terraform module again
terraform apply
- Invoke the function with a test event:
aws lambda invoke --function-name my-function-terraform-"$TF_VAR_aws_user" out --payload '{ "jokeID": "1" }' --log-type Tail --query 'LogResult' --output text | base64 -d
- Inspect the traces that have been created during the last 20 minutes:
aws xray get-service-graph --start-time $(($(date +"%s") -1200)) --end-time $(date +"%s")
- Paste code
To reach level 4, you will need to reduce the cold start time of your function. Warmers are usually not recommended but you can try moving initialization code outside of your handler.
- Go to the AWS Lambda UI
- Click on
Functionsin the left navigation - Choose the function
my-function-AWSUSER, which you updated in level 3 - Copy the code from ./level-4/function/index.js and paste it over the existing code in the editor field
- Press the
Deploybutton - Press the
Testbutton and create a test event calledjoke - Paste the following JSON object to the editor field
{
"jokeID": "1"
}
- Press the
Testbutton again to run the test - Observe the test output
Try it with the AWS CLI!
- Make sure the AWSUSER and ACCOUNT_ID environment variables are still set.
export AWSUSER=<your AWS username>
export ACCOUNT_ID=<your account ID>
- Create a deployment package for your new function:
zip -j function.zip level-4/function/index.js
- Update the function with the new code:
aws lambda update-function-code --function-name my-function-cli-"$AWSUSER" --zip-file fileb://function.zip
- Invoke the function with a test event:
aws lambda invoke --function-name my-function-cli-"$AWSUSER" --cli-binary-format raw-in-base64-out --payload '{ "jokeID": "1" }' out --log-type Tail
Still bored? Then try it with Terraform!
- Make sure your work directory and user variables are still set
export WORKDIR=<your work directory>
export TF_VAR_aws_user=<your AWS user name>
- Copy the updated function code to your working directory
cp -r level-4/function $WORKDIR
- Apply the Terraform module again
terraform apply
- Invoke the function with a test event:
aws lambda invoke --function-name my-function-cli-"$AWSUSER" --cli-binary-format raw-in-base64-out --payload '{ "jokeID": "1" }' out --log-type Tail