Manual deployment of a Lambda function

The Node.js Lambda application that we will be using here is already part of AWS tutorials. We will learn how to create, deploy, and execute a Lambda application via the AWS portal. The prerequisite for this tutorial is for you to have an AWS account; we will be using a free AWS subscription throughout this chapter. The next step is to set up AWS CLI.

You can create an AWS free account and an AWS CLI through the following links: 
https://portal.aws.amazon.com/billing/signup#/start
https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-welcome.html

Go through the following steps:

  1. Once the AWS account and CLIs are in place, sign in to AWS Console (https://aws.amazon.com/console/), and then we will create an IAM user with the name adminuser by logging into your AWS account then either clicking on the IAM link or searching for the link through the services:
  1. Then we click on the Users link on the left-hand side, where a new page will open. Then we click on Add User. Add a user with the name adminuser and select both the access type Programmatic access and AWS Management Console access. In the console's Password field, add your custom password, uncheck the Require password reset checkbox, and then click Next:Permissions. Let's create a group by clicking on the Create Group button. We will give the group the name of administrators. Next, let's select the AdminstrativeAccess checkbox to provide full access to the group and then click on Create Group. Now that we have a group created, let's click on Next:Review. Then, we will review the user, which the user have created and has been added to the administrator group. Now click on the Create User button. Once the user is created, we should be able to see the user in the list, as shown in the following screenshot:
We have created a user with administrative rights just for our tutorials, but in the real world the role and policies will be more restricted for the sake of security. 
  1. We need to create two buckets in AWS S3. The buckets need to be created through the adminuser login, so let's log in to AWS Console using the new user that we have created. Click on the adminuser and select the Security credentials tab. Next, let's copy the Console URL for logging in, then open it on the new browser tab. Feed in the username and password for the new user that we have created and click on Sign InOnce you are logged in, search for S3 in the AWS Services. Then go to S3 Console Management and click on the Create bucket button, as shown in the following screenshot:

Let's add a unique bucket and region name of US East by default. The two buckets should be named Source and SourceResized. Source is the placeholder name that should be replaced with the actual bucket name—for example, my-image-bucket76 and my-image-bucket76resizedSo, my-image-bucket76 will be the source bucket and my-image-bucket76resized will be the target bucket, as shown in the following screenshot:

The bucket name must be unique, as AWS allows only universally unique bucket names.
  1. Once both the buckets are successfully created, we can upload an image for the Lambda function to resize and push to the resized bucket. Let's upload a JPG image into the my-source-bucket76 source. Click on the bucket name, then upload an image to this bucket. This will redirect you to the bucket page. Click on the Upload button and a popup will pop up. Then, select Add files to browse for an image file from the local directory and then upload the image to the S3 bucket.
  2. The next step is to create a Lambda function and run it manually. Here, we have to first follow three steps to create the deployment package, and then create the execution role (IAM role) and a Lambda function and test them. The deployment package is a ZIP file containing the Lambda function and its dependencies:
    • Let's create a deployment package. I will be using Node.js as the language for this practice application, but we can use Java and Python as well (it depends on the developer's preference).
    • A prerequisite for creating this deployment package is to have Node.js version 6.0 (https://nodejs.org/en/download/) or later installed on you local environment. You should also make sure that npm is installed. 
    • Then go through the following steps. We are downloading the npm libraries for the image resizing of the Node.js Lambda function, using the first of the two following commands. The second command will download the required libraries:
$ mkdir tutorial1; cd tutorial1
$ npm install async gm
    • Open your favorite editor and copy the following script into the file named CreateThumbnails.js:
// dependencies
var async = require('async');
var AWS = require('aws-sdk');
var gm = require('gm')
.subClass({ imageMagick: true }); // Enable ImageMagick integration.
var util = require('util');
// constants
var MAX_WIDTH = 100;
var MAX_HEIGHT = 100;
// get reference to S3 client
var s3 = new AWS.S3();
exports.handler = function(event, context, callback) {
// Read options from the event.
console.log("Reading options from event:\n", util.inspect(event, {depth: 5}));
var srcBucket = event.Records[0].s3.bucket.name;
// Object key may have spaces or unicode non-ASCII characters.
var srcKey =
decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, " "));
var dstBucket = srcBucket + "resized";
var dstKey = "resized-" + srcKey;
You can find the gist for CreateThumbnails.js at  https://gist.github.com/shzshi/6e1cf435a4c1aa979e3a9a243c13c44a.
    • As a sanity check, validate that the source and destination are different buckets:
    if (srcBucket == dstBucket) {
callback("Source and destination buckets are the same.");
return;
}
    • Infer the image type:
var typeMatch = srcKey.match(/\.([^.]*)$/);
if (!typeMatch) {
callback("Could not determine the image type.");
return;
}
var imageType = typeMatch[1];
if (imageType != "jpg" && imageType != "png") {
callback('Unsupported image type: ${imageType}');
return;
}
    • Download the image from S3, transform it, and upload it to a different S3 bucket:
async.waterfall([
function download(next) {
// Download the image from S3 into a buffer.
s3.getObject({
Bucket: srcBucket,
Key: srcKey
},
next);
},
function transform(response, next) {
gm(response.Body).size(function(err, size) {
    • Infer the scaling factor to avoid stretching the image unnaturally:
var scalingFactor = Math.min(
MAX_WIDTH / size.width,
MAX_HEIGHT / size.height
);
var width = scalingFactor * size.width;
var height = scalingFactor * size.height;
    • Transform the image buffer in memory:
this.resize(width, height)
.toBuffer(imageType, function(err, buffer) {
if (err) {
next(err);
} else {
next(null, response.ContentType, buffer);
}
});
});
},
function upload(contentType, data, next) {
    • Stream the transformed image to a different S3 bucket:
s3.putObject({
Bucket: dstBucket,
Key: dstKey,
Body: data,
ContentType: contentType
},
next);
}
], function (err) {
if (err) {
console.error(
'Unable to resize ' + srcBucket + '/' + srcKey +
' and upload to ' + dstBucket + '/' + dstKey +
' due to an error: ' + err
);
} else {
console.log(
'Successfully resized ' + srcBucket + '/' + srcKey
+
' and uploaded to ' + dstBucket + '/' + dstKey
);
}
callback(null, "message");
}
);
};

Now we should see two  items in our tutorials1 folder, namely the CreateThumbnail.js and node_modules folders. Let's zip all these into a file named tutorialsimg.zip. This will be our Lambda function deployment package:

$ cd tutorials1
$ zip -r ../tutorialsimg.zip *
  1. Next, we will create the execution role for the Lambda function in IAM. Log into AWS Console, search for IAM services, and then go into IAM and click on the Roles button. Click on Create Role, select the AWS service, and then choose Lambda as the service. Then click on the Next: Permission button. Then search for AWSLambda in the Policy Type box and check the AWSLambdaExecute checkbox. Then search for AmazonS3FullAccess and select it. Then click on the Next:Review button. Next, on the Create Role page, add the role name myLambdaRole, add a description, and then click on Create Role. Now we have the role that contains the policies that we can use to execute the Lambda function in order to make changes to the content of the S3 bucket. 
  2. The next step is to deploy the Lambda functions and node modules on the AWS Lambda portal. Let's go to the AWS Console's home page, and under Services, let's search for Lambda. We will be redirected to the Lambda home page. Click on Create Function, then choose Author from scratch in the Functions Information section. Let's add the function name of myfirstLambdafunction, Runtime as Node.js 6.10. Choose the existing role name of myLambdaRole and click on Create Function. Now we will be redirected to the Lambda function designer page. We will upload our Lambda function to the portal. Scroll down a bit and go to the Function Code section, then select Code entry type as Upload a .ZIP file, Runtime as Node.js 6.10, and type into the Handler field the text CreateThumbnail.handler. Now let's click on the Upload button, select the file named tutorialsimg.zip and upload it. Once the package is uploaded successfully, we should be able to see the CreateThumbnail.js file in the function editor with the node_modules folder, as shown in the following screenshot:
  1. Now that the function and node modules are uploaded, we will create an event to trigger the function. If we scroll to the top right-hand side of the screen, we will see a drop-down menu appear that we can use to add the event. Select Configure Test Events. A pop-up box will appear. Give the event the name of myThumbnailEvent and in the text field, add the following listed JSON file. Make sure that you replace my-source-bucket76 with your source bucket's name and Baby.jpg with your image name. Then go ahead and click Save:
{
"Records": [
{
"eventVersion": "2.0",
"eventSource": "aws:s3",
"awsRegion": "us-west-2",
"eventTime": "1970-01-01T00:00:00.000Z",
"eventName": "ObjectCreated:Put",
"userIdentity": {
"principalId": "AIDAJDPLRKLG7UEXAMPLE"
},
"requestParameters": {
"sourceIPAddress": "127.0.0.1"
},
"responseElements": {
"x-amz-request-id": "C3D13FE58DE4C810",
"x-amz-id-2": "FMyUVURIY8/IgAtTv8xRjskZQpcIZ9KG4V5Wp6S7S/JRWeUWerMUE5JgHvANOjpD"
},
"s3": {
"s3SchemaVersion": "1.0",
"configurationId": "testConfigRule",
"bucket": {
"name": "my-source-bucket76",
"ownerIdentity": {
"principalId": "A3NL1KOZZKExample"
},
"arn": "arn:aws:s3:::my-source-bucket76"
},
"object": {
"key": "Baby.jpg",
"size": 1024,
"eTag": "d41d8cd98f00b204e9800998ecf8427e",
"versionId": "096fKKXTRTtl3on89fVO.nfljtsv6qko"
}
}
}
]
}
  1. Now we have deployed the function, and have created S3 buckets and an event. Now let's invoke the function and see if our image in the source bucket is resized and pushed to the resized S3 bucket. To do this, click on Test. We should see that the function has successfully executed in the logs (as shown in the following log text). If you refresh the resized named S3 bucket, you should be able to see resized image file. You can just download the resized file and see whether the resizing worked. We can also add the S3 put trigger to automatically trigger this CreateThumbnail function when any image file is uploaded to the source S3 bucket. 
eTag: 'd41d8cd98f00b204e9800998ecf8427e', versionId: '096fKKXTRTtl3on89fVO.nfljtsv6qko' } } } ] } 2018-06-14T21:07:25.469Z ea822830-7016-11e8-b407-9514918aacd8

Successfully resized my-source-bucket76/Baby.jpg and uploaded to my-source-bucket76resized/resized-Baby.jpg END RequestId: ea822830-7016-11e8-b407-9514918aacd8

In this exercise, we learned how to create, build, deploy, and invoke the Lambda function manually, and we had to go through number of steps to get this working. Now let's say that we need to deploy hundreds or thousands of such Lambda functions for a banking application to the portal. To do this task manually, we would require lots of resources and time. This is where DevOps comes in really handy to make our life faster and more easy. Let's look closer at how we can use DevOps to automate all of the steps involved and make it much simpler to build, test, and deploy our Lambda functions.