Serverless run times with custom Bash AWS Lambda layers

A Code Bash

All Got A Role To Play

Before creating a function and attaching the Bash layer, you need an AWS IAM role to provide permissions for its execution ("invocation" in AWS Lambda-speak) and to allow it to write to CloudWatch logs correctly. Note that these permissions are created by default if you create a function inside the AWS Management Console, so re-use those if you get stuck. Also note that Elastic Network Interface creation, deletion, and description of VPC permissions are also needed if you plan to use a VPC with your Lambda function so it can access internal AWS resources (e.g., Elastic Compute Cloud (EC2) instances and the like).

Listing 2 shows the IAM permission policy to use with your role. You need to replace the XXXXX entries with your own AWS account number to keep permissions tight. You can find the aforementioned account number by clicking the top-right, pull-down menu in the AWS Management Console and looking for Billing in the options. Save Listing 2 to your filesystem as policy.json. Note that bashFunction under Resource is the name of your function and needs to be changed if you change the function name.

Listing 2

policy.json

01 {
02     "Version": "2012-10-17",
03     "Statement": [
04         {
05             "Effect": "Allow",
06             "Action": "logs:CreateLogGroup",
07             "Resource": "arn:aws:logs:eu-west-1:XXXXX:*"
08         },
09         {
10             "Effect": "Allow",
11             "Action": [
12                 "logs:CreateLogStream",
13                 "logs:PutLogEvents"
14             ],
15             "Resource": [
16                 "arn:aws:logs:eu-west-1:XXXXX:log-group:/aws/lambda/bashFunction:*"
17             ]
18         }
19     ]
20 }

For the IAM role, you will also need a trusted entity to tell AWS which services can use this role. Listing 3 names Lambda (!) as the service of choice. Call this file trust.json.

Listing 3

trust.json

01 {
02   "Version": "2012-10-17",
03   "Statement": [
04     {
05       "Effect": "Allow",
06       "Principal": {
07         "Service": "lambda.amazonaws.com"
08       },
09       "Action": "sts:AssumeRole"
10     }
11   ]
12 }

You're almost there. You just need to create a role with that trust policy attached to it; then, you'll attach a permission policy. The command that creates the role with that trust policy is:

$ aws iam create-role --role-name bashFunctionRole --assume-role-policy-document file://trust.json

Any illegal character errors you receive might be caused by cut and paste errors [10], so check your input.

If that command is successful, you'll see lots of output, including a RoleId. If you want to be certain that role does exist, check the IAM section of the AWS Management Console.

Next, create a permissions policy in AWS and attach it to your role (Listing 2). Listing 4 creates a permissions policy called bashFunctionPolicy in AWS with the policy.json code.

Listing 4

Permissions Policy (Redacted)

$ aws iam create-policy --policy-name bashFunctionPolicy --policy-document file://policy.json
{
    "Policy": {
        "PolicyName": "bashFunctionPolicy",
        "PermissionsBoundaryUsageCount": 0,
        "CreateDate": "2001-11-11T11:11:11Z",
        "AttachmentCount": 0,
        "IsAttachable": true,
        "PolicyId": "ABCDEFGHIJKLMNOPQ",
        "DefaultVersionId": "v1",
        "Path": "/",
        "Arn": "arn:aws:iam::XXXXX:policy/bashFunctionPolicy",
        "UpdateDate": "2001-11-11T11:11:11Z"
    }
}

The next command attaches the policy to your IAM role. (Note that I've replaced my AWS account number with Xs again for security reasons.)

$ aws iam attach-role-policy --policy-arn arn:aws:iam::XXXXX:policy/bashFunctionPolicy --role-name bashFunctionRole

You might be a little surprised to see that the successful execution of that command is a simple, empty newline with no output whatsoever. To verify, check in the IAM section of the AWS Management Console to see that it worked. Figure 1 proves, having looked up the role name in IAM, that it works as hoped.

Figure 1: Happiness is an attached policy, as seen in the AWS Management Console.

So Close …

Now you can create a shiny new Lambda function and attach your Bash layer by using the AWS CLI to execute the long aws lambda create-function command shown at the beginning of this article. When confronted with long commands, I can find them tricky to cut and paste, so I often just drop them into a script (adding #!/bin/bash as the first line), which also makes them easier to tweak and reference later. To use this script, save it as a file called run.sh (Listing 5) and make it executable with:

$ chmod +x run.sh

Listing 5

run.sh

01 #!/bin/bash
02
03 aws lambda create-function \
04     --function-name bashFunction \
05     --role arn:aws:iam::XXXXX:role/bashFunctionRole \
06     --handler index.handler \
07     --runtime provided \
08     --layers arn:aws:lambda:eu-west-1:744348701589:layer:bash:8 \
09     --zip-file fileb://list_buckets.zip

In my case, the only things I replace are the <region> entry with eu-west-1 and function.zip to the newly named ZIP payload with the filename list_buckets.zip. To run that unwieldy command, enter:

$ ./run.sh

If you get stuck, you can copy and paste the code in Listing 5 (which is available on FTP [10]) and replace the Xs with your AWS account number, as usual.

The output from this relatively long command is shown in Listing 6, which shows that I've created a new Lambda function (in Dublin, Ireland), added a layer to it from another account, and given it permissions from a role with a policy attached to it. (Again, the output is redacted a bit.) The next step invokes the function and checks its output.

Listing 6

lambda create-function Output

01 {
02     "Layers": [
03         {
04             "CodeSize": 26955425,
05             "Arn": "arn:aws:lambda:eu-west-1:744348701589:layer:bash:8"
06         }
07     ],
08     "FunctionName": "bashFunction",
09     "LastModified": "2001-11-11T11:11:11Z",
10     "RevisionId": "aa63-b5c0-4ec2-a5e99",
11     "MemorySize": 128,
12     "Version": "$LATEST",
13     "Role": "arn:aws:iam::XXXXX:role/bashFunctionRole",
14     "Timeout": 3,
15     "Runtime": "provided",
16     "TracingConfig": {
17         "Mode": "PassThrough"
18     },
19     "CodeSha256": "37n/rzJz4o2lyvh4s2aet2aBlY=adc",
20     "Description": "",
21     "CodeSize": 432,
22     "FunctionArn": "arn:aws:lambda:eu-west-1:XXXXX:function:bashFunction",
23     "Handler": "index.handler"
24 }

Alrighty Then

Of course, you can send variables to the Lambda function to execute it from the AWS CLI, but you should look at the AWS Management Console, as well, so you can get used to how a function looks there. In my case, I would navigate to https://eu-west-1.console.aws.amazon.com/lambda/home?region=eu-west-1#/functions in my Browser, which should list, among my other functions, bashFunctionRole . The eagle-eyed also will spot Custom runtime , on which I'll click to enter its configuration.

Once on that screen (Figure 2), double-click on the index.sh script. You can see that AWS Lambda has read the ZIP file payload as hoped and displays it on request.

Figure 2: Your payload is waiting and decompressed for direct editing, if needed.

Jump back up the page a moment, though, expand the little black menu arrow under the word Designer , and click on the attached layer. All in all, you should be able to see the config (Figure 3, blue highlight).

Figure 3: The layer exists, as expected, and is attached with the name bash (bottom line). The AWS account number is where the layer resides.

The next thing to check is whether the permissions are as intended. If you scroll down the very long page, you can see the attached role (Figure 4). Clicking on the link lets you see that your role has your policy attached.

Figure 4: The preferred role is indeed there and in use by the Lambda function. Clicking the link lets you verify that the policy is attached, as expected, if you're unsure.

The last stage requires invoking your new Lambda function's payload, written in the Bash scripting language and sitting atop your custom run time, to see what happens.

Buy this article as PDF

Express-Checkout as PDF
Price $2.95
(incl. VAT)

Buy ADMIN Magazine

SINGLE ISSUES
 
SUBSCRIPTIONS
 
TABLET & SMARTPHONE APPS
Get it on Google Play

US / Canada

Get it on Google Play

UK / Australia

Related content

comments powered by Disqus
Subscribe to our ADMIN Newsletters
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs



Support Our Work

ADMIN content is made possible with support from readers like you. Please consider contributing when you've found an article to be beneficial.

Learn More”>
	</a>

<hr>		    
			</div>
		    		</div>

		<div class=