Lambda, Terraform example

This article will walk through the steps of setting up an AWS Lambda function. We will use Terraform to create the lambda function and associated IAM policies.

IAM Policy and roles

Like many cloud providers, to successfully invoke a server-less function in the cloud, the user must have the appropriate roles and policies. In AWS, for a user to invoke a server-less (Lambda), the user must have the following:

  1. Ability to assume STS role for Lambda - this is needed for the user to be able to interact with AWS Lambda.
  2. Policies to “allow” the Lambda function to be invoked.
  3. Policies to “allow” Cloudwatch logging.

Below is an example of the Terraform script for a similar setup:

Policy document with policies to allow role to invoke Lambda function matching the ARN. The policy document is linked to user's role.

To enable Cloudwatch logging, add the following statement to the “aws_iam_policy_document . role_policy_doc”.

1
2
3
4
5
statement {
effect = "Allow"
actions = ["logs:CreateLogGroup","logs:CreateLogStream","logs:PutLogEvents"]
resources = ["ars:aws:logs:*:*:"]
}

Testing Policies

AWS IAM Policy Simulator lets you validate the policies and roles for your Lambda function. The image below shows how to test permissions that are enabled for a particular Lambda function resource.

Policy simulator, testing document policy for lambda

Simulation results.

Lambda function

When creating a Lambda function, the role is specified. This is the user role that runs the function when the function is invoked. The “handler” specifies the entry point to the Lambda function and uses the format of file.function. In this example, this Lambda function will use a file names exports and call a function named handler.

Terraform detailing how the Lambda function should be created.

Lambda function handler

Even though were are writing in JavaScript for the nodeJs engine, there are limitations on what libraries you can import into your application. For example commonly used libraries such as ‘axios’ are not available for making HTTP post request from Lambda.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
const https = require('https');
const qs = require('querystring');

exports.handler = async (event) => {

// https post request with form-data.

const getAuthToken= () => new Promise((resolve,reject) => {
const authOptions = {
method:'POST',
host: 'auth.example.com',
path: '/token',
headers: {'Content-Type':'application/x-www-form-urlencoded'}
};
const req = https.request(authOptions, res => {
let buffer = "";
res.on('data', chunk => buffer += chunk);
res.on('end', () => resolve(JSON.parse(buffer)));
});
res.on('error', e=> reject(e.message));
const postData = qs.stringify({
'grant_type':'...'
});
req.write(postData);
req.end();
});

// https post request

const doHttpPost = (id, jwtToken) => new Promise((resolve,reject) => {
const options = {
method:'POST',
host: 'example.com',
path: '/something',
headers: {
'Content-Type':'application/json',
'Authorization' : `Bearer ${jwtToken}`
}
};
const req = https.request(authOptions, res => {
let buffer = "";
res.on('data', chunk => buffer += chunk);
res.on('end', () => resolve(JSON.stringify(buffer)));
});
res.on('end', () => resolve(e.message));
req.end();
});

// Start
const accessToken = await getAuthToken();
const id = event.id;
const result = await doHttpPost(id, accessToken);
};

source - IAM policy simulator
source - Terraform documentations for lambda permissions
source - access control for AWS lambda