Create and deploy AWS Lambda function in GOlang

In this article, I will walk you through how to create and deploy AWS Lambda functions using Go. But what exactly are aws lambda functions? what purpose they serve? how can we create and deploy them effortlessly. This post aims to find the answers to these questions.

AWS Lambda is a highly available and scalable compute service. It runs function code written in many programming languages like GO, Java, NodeJS, etc, without managing and provisioning the servers. This makes the developers focus more on business logic of the application/system rather than platform complexities. But writing and automating the deployment of the AWS Lambda function is not quite straightforward. There are several moving parts that are tricky to grasp in the beginning.

This article will help as your go-to resource, offering a detailed, step-by-step guide on creating, packaging, deploying, and running AWS Lambda functions seamlessly using Cloudformation, AWS CLI, and Go language. It covers the steps to,

  • Create and initialize a Go module
  • Create an AWS Lambda function in that module
  • Build the module on different platforms
  • Package the Lambda function code into an archive file (.zip)
  • Create an AWS S3 bucket to host the Lambda function archive file
  • Import the archive file to the S3 bucket
  • Create CloudFormation template and automatically creating necessary AWS resources
  • Test the lambda function
  • Check logs in AWS CloudWatch
  • Delete the CloudFormation stack to clean up the resources

Prerequisites:

Having all prerequisites, let’s have some hands-on experience of Lambda function.

Create a Go module

First of all, create a directory for your project code, and then initialize a GO module by following command,,

go mod init <your-namespacel>/aws-lambda-demo-go

Provide any preferred namespace, <your-namespacel>/aws-lambda-demo-go

If you are not sure how to setup Go working environment then this blog will really help you.

go mod init creates a go.mod file in your project directory, where we define all other dependencies required by our module. First library we need for this project is aws-lambda-go library,

module <your-namespacel>/aws-lambda-demo-go

go 1.22

require (
	github.com/aws/aws-lambda-go v1.46.0
	github.com/aws/aws-sdk-go v1.51.21
)

require github.com/jmespath/go-jmespath v0.4.0

Resolve the dependencies by running,

go mod tidy
go mod vendor

Now create an event handler function HandleRequest and start it inside main(). AWS Lambda will trigger the HandleRequest function on an external event.

package main

import (
"context"
"fmt"

"github.com/aws/aws-lambda-go/lambda"
)

type MyBook struct {
ID string `json:"id,omitempty"`
Title string `json:"title,omitempty"`
}

func HandleRequest(ctx context.Context, book MyBook) (string, error) {
msg := fmt.Sprintf("ID: %s, Title: %s", book.ID, book.Title)
fmt.Println(msg)

return msg, nil
}

func main() {
lambda.Start(HandleRequest)
}

We defined a struct named MyBook to unmarshal incoming event.

The HandleRequest function takes in two parameters: Context, which contains request details, and MyBook, which is an object. The function simply prints out the MyBook object and returns it as string. You can expand this function with your own business logic.

Building GO Module

We are done with writing our function code, now time to build it,

GOOS=linux go build main.go

Setting GOOS to Linux ensures that the compiled executable is compatible with the Go runtime, even if you compile it in a non-Linux environment[1].

Packaging function

There are several methods to deploy function code to AWS Lambda. You can utilize the Lambda code editor within the AWS Management Console, or can upload a code archive file to an S3 bucket and direct Lambda to retrieve the code from that location. In this excercise, we'll follow later approach.

We will use build-lambda-zip tool to create .zip file, let's install it,

On Windows:

go install github.com/aws/aws-lambda-go/cmd/build-lambda-zip@latest

Next, set the necessary environment variables for your Go build:

set GOOS=linux
set GOARCH=amd64
set CGO_ENABLED=0

Add the path to the build-lambda-zip tool (%USERPROFILE%\Go\bin) to your GOPATH environment variable. This ensures that the tool is accessible from anywhere on your system.

Download the lambda library from GitHub,

go get github.com/aws/aws-lambda-go/lambda

Finally create a deployment package (.zip file) by build-lambda-zip,

build-lambda-zip.exe -o aws-lambda-demo-go.zip main

On Linux and Mac OSX:

Download the lambda library from GitHub,

go get github.com/aws/aws-lambda-go/lambda

Compile executables:

GOOS=linux GOARCH=amd64 go build -o main main.go

ZIP the package

Zip main.zip main

Importing archive to S3

As written earlier, in this example we will import an archive (.zip) of the lambda function code to s3 bucket.

If you do not already have a S3 bucket, then create a new by AWS CLI command,

aws s3api create-bucket - bucket my-demo-s3-bucket --create-bucket-configuration LocationConstraint=eu-west-1

Upload zip file to aws S3 bucket using AWS CLI

aws s3 cp aws-lambda-demo-go.zip s3://my-demo-s3-bucket/demo-lambda/aws-lambda-demo-go.zip

Deploy Lambda function using Cloudformation:

Setting up Lambda functions and configuring roles and policies can be challenging, and manually deploying them can lead to errors. AWS CloudFormation simplifies this by automating the creation of resource stacks, reducing the likelihood of mistakes. It enables faster, repeatable deployment and provisioning of cloud resources through Infrastructure as Code.

With CloudFormation, you can create an AWS resource template using JSON or YAML. This template includes all the resources your application or backend stack requires, such as Lambda functions, API Gateway, SNS, SQS, EC2 instances, VPC, DynamoDB, security groups, roles, and policies.

You can validate and prettify your json object using this online tool.

We create following cloudformation template:

{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "A CF template to create a lambda function",
"Parameters": {
"pLambdaCodeBucket": {
"Type": "String"
},
"pLambdaCodeS3KeyPath": {
"Type": "String"
}
},
"Resources": {
"lfnLambdaRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
},
"Policies": [
{
"PolicyName": "observability",
"PolicyDocument": {
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "*"
}
]
}
}
]
}
},
"lfnMyDemoLambda": {
"Type": "AWS::Lambda::Function",
"DependsOn": [
"lfnLambdaRole"
],
"Properties": {
"Architectures": [
"x86_64"
],
"Runtime": "go1.x",
"Handler": "main",
"Code": {
"S3Bucket": {
"Ref": "pLambdaCodeBucket"
},
"S3Key": {
"Ref": "pLambdaCodeS3KeyPath"
}
},
"Description": "This is my demo lambda function",
"FunctionName": "my-demo-lambda",
"Role": {
"Fn::GetAtt": [
"lfnLambdaRole",
"Arn"
]
},
"Timeout": "120"
}
}
}
}

In this template, we define a Lambda function (Type: AWS::Lambda::Function) named my-demo-lambda in the lfnMyDemoLambda section (logical name). The function's code is stored in an archive file located in an S3 bucket ("Ref": "pLambdaCodeBucket") and identified by a file key ("Ref": "pLambdaCodeS3KeyPath").

We also define an IAM role, lfnLambdaRole (“Type”: “AWS::IAM::Role”), which is required by the lambda function to execute in the AWS environment (“Service”: “lambda.amazonaws.com”).

In the IAM role lfnLambdaRole we also define a Policy (“PolicyName”: “observability”) to create Cloudwatch logs. This is very handy to troubleshoot lambda functions.

We are ready to deploy this cloudformation stack,

aws cloudformation deploy \
--template-file ./deploy/cf.json \
--stack-name my-demo-lambda-stack \
--capabilities CAPABILITY_IAM \
--parameter-overrides \
pLambdaCodeBucket=azam-demo-s3-bucket \
pLambdaCodeS3KeyPath=demo-lambda/aws-lambda-demo-go.zip

Note: pLambdaCodeBucket and pLambdaCodeS3KeyPath are parameter names, passed to Cloudformation deploy command, which is a way to pass dynamic values to CF template. Parameters can be read by the special function Ref as written below.

This should trigger a Cloudformation stack deploy, which may take few minutes depending on how many resources we are going to deploy,

Creating and deploying AWS Lambda function made easy in GOlang

After the deployment is complete, you should be able to find the newly created Lambda function. Navigate to the Lambda service in the AWS Management Console and search for the function using its name, in this instance, my-demo-lambda

Test Lambda Function

AWS Management Console provides built-in functionality to Test lambda function by pushing an input event. In this case, we pass JSON event to the function HandleRequest which will be unmarshalled to MyBook struct,

If you follow all the above steps correctly, your Test should successfully trigger the lambda function.

Cloudwatch logs:

In the previous section, we assigned an IAM role to the Lambda function to enable CloudWatch logging. After testing the function, we can view its logs in CloudWatch. To do this, navigate to CloudWatch in the AWS Management Console, click on 'Log groups' in the left menu, and filter log groups by the Lambda function name, such as /aws/lambda/my-demo-lambda. Choose one of the log streams to view the logged items.

Delete stack:

If you wish to remove all resources deployed as part of the CloudFormation stack, AWS CloudFormation offers the 'delete-stack' command for this purpose:

aws cloudformation delete-stack - stack-name my-demo-lambda-stack

Note: In this example, if an S3 bucket was created outside of the CloudFormation stack using the AWS CLI, the 'delete-stack' command will not clean up the S3 bucket. You will need to manually remove it if it's no longer needed."

Final Words:

If you have followed all the above steps correctly, you should be able to create, package and deploy your first AWS Lambda function.

I would recommend building the knowledge obtained in this article and reading my next article, Adding CRUD operations in AWS Lambda and DynamoDB using GOlang

I trust that you've successfully completed the steps outlined above and have created, packaged, and deployed your first AWS Lambda function. Building upon the knowledge gained from this article, I encourage you to go deeper into serverless architecture by exploring my next article, which covers the implementation of CRUD operations in AWS Lambda and DynamoDB using Go Language. This will further enhance your understanding and proficiency in developing serverless applications on the AWS platform

© 2024 Solution Toolkit . All rights reserved.