In my last article, titled "What is serverless computing? An introduction to AWS Lambda," we took a detailed look at what serverless computing does. Taking the next step, we will create a working example in AWS Lambda using the Go programming language (often referred to as Golang).
Latest posts by Graham Beer (see all)

For the example, we will create a Lambda function that takes an array of EC2 instance IDs and returns their current status. The Lambda function will be written using AWS SDK for Go V2.

Getting started with AWS Lambda using Go

Go is a great fit when it comes to AWS Lambda. Go brings the advantage of type safety, as it is a statically typed language, lightweight, and performant code, and has a robust tooling system built in.

To follow along, you will need to have the following installed:

  1. Go 1.15 or later to use the Go AWS SDK V2
  2. AWS account
  3. Access and secret keys for programmatic access to AWS
  4. The AWS CLI installed

Building an AWS Lambda function with Go

Initial setup

If you have experience with AWS Lambda in another language, such as Node.js or Python, then you will have used the Lambda function editor to create your program. Go is a compiled language, meaning we will have to build our Lambda function slightly differently than with a scripting language. This certainly does not mean that it is harder to build. We will build our Lambda function though our favorite IDE. In my case, this is VSCode.

All resources can be created by the AWS CLI, but for this starter Lambda, we will create our Lambda resource through the AWS console.

Sign into your AWS account via the console, and navigate to the Lambda service.

  • Click Create function.
Creating a Lambda resource

Creating a Lambda resource

The Create function form is displayed. For this demo:

  • Choose Author from scratch. This allows us to code our own function.
  • Set the name of our function; here, I've named it ec2-status-check.
  • Set the runtime we are choosing to work with; in our case, it's Go 1.x. With many languages in Lambda, you would have to select a minor version. With Go, we are free to use any version from 1. I could use 1.12, or the latest and greatest, 1.18.
  • We have the option to add our own IAM role or set up a basic Lambda role. Select Create a new role from AWS policy templates and name the role MyDescribeEC2. We are working with EC2s and want to be able to describe the instance status. This will create a role with the basic Lambda permissions; it will also add an additional custom policy later on.

Finally, click Create function to finish the initial setup.

  • Now that the function is created, change the handler name from the runtime settings by clicking Edit.
Changing the handler name from the runtime settings

Changing the handler name from the runtime settings

  • Before we move on to our code, we need to add a custom policy to allow the Lambda function permission to get the EC2 status. Under Function overview, select the Configuration tab. Under General configuration, click Edit
    General configuration from a Lambda function

    General configuration from a Lambda function

  • In the Basic settings, scroll to the bottom of the screen and click View the MyDescribeEC2 role. This opens a new browser window with the details of our IAM role.
General Configuration basic settings

General Configuration basic settings

  • From Permission policies, select Add permissions from the right side of the screen and choose Create inline policy from the dropdown list.
  • In the Create policy window, select the JSON tab and paste the snippet below:
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstances"
            ],
            "Resource": "*"
        }
    ]
}

The policy action allows us to just describe the EC2 instance, which is all that is required for this Lambda.

The Lambda function code

Let's take a look at the folder structure for this small project:

Project directory structure

Project directory structure

The main part of the code is in cmd. This folder contains our main application, and since this is a small project, this is our only application file. The compiled program will go to the build directory.

From the local machine, create a directory named aws-lambda-go and change the directory to it.

NOTE: All commands in this article will be written in bash.

mkdir aws-lambda-go && cd aws-lambda-go

In our folder, we need to initialize our Go project with a module file. The go.mod file reference states

"Each Go module is defined by a go.mod file that describes the module's properties, including its dependencies on other modules and on versions of Go."

We do this using the Go command line.

Go mod init aws-lambda-go

For more information on the Go module, see the official guide.

Now we have initialized our Go module project.

The next area is the code itself, inside main.go.

package main

import (
	"context"
	"fmt"

	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/ec2"
)

type Instance struct {
	InstanceIDs []string `json:"InstanceID"`
}

var client *ec2.Client

func init() {
	cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("eu-west-1"))
	if err != nil {
		panic("configuration error, " + err.Error())
	}

	client = ec2.NewFromConfig(cfg)
}

func HandleRequest(instances Instance) ([]string, error) {

	result, err := client.DescribeInstances(context.TODO(), &ec2.DescribeInstancesInput{
		InstanceIds: instances.InstanceIDs,
	})
	if err != nil {
		return []string{}, err
	}

	var status []string
	for _, r := range result.Reservations {
		for _, i := range r.Instances {
			status = append(status, fmt.Sprintf("InstanceID: %v State: %v", *i.InstanceId, i.State.Name))
		}

		fmt.Println("")
	}

	return status, nil
}

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

To help us understand what the code is doing, we will go through some key lines.

Since we have initialized our Go project, we need to add Go SDK dependencies. This is done from our project directory using the Go command:

  • go get github.com/aws/aws-lambda-go/lambda
  • go get github.com/aws/aws-sdk-go-v2/config
  • go get github.com/aws/aws-sdk-go-v2/service/ec2

We use a struct named Instance on line 12, which is an input type for our HandleRequest function. This value will be returned in the return statement.

On line 18, the init function is used to provide an initial setup of our Lambda. Using the config dependency from the SDK allows us to pass in a region and credentials, among other properties. This defines the standards for our program.

Line 27 provides the main function for our program, HandleRequest. HandleRequest is where the state details of the EC2 instances are captured. The DescribeInstancesInput method takes the Instance IDs passed to gather information. The function loops through the instances to display the state method of the EC2.

The final part of the Lambda is the entry point for running our code. We pass our HandleRequest to the Lambda function to execute, lambda.Start(HandleRequest).

Compile time

Our Lambda function is now written, but before we can upload the code to AWS Lambda, it needs to be compiled and then zipped up.

The Go build command is used to compile our executable. The Lambda will be run on a Linux OS, so we need to make sure our Lambda application complies with Linux. To do so, we set the Go variable GOOS to configure the execution operating system. The command uses the switch -o to output the compiled file to our build directory, called main. We are compiling only a single file, which is shown at the end of the command:

GOOS=linux go build -o build/main cmd/main.go

The next step is to zip the compiled file before uploading it through the AWS Lambda console. I'm using Zip 3.0 for Linux and Mac OS, but any compression tool will work as long as the file extension ends with .zip.

zip -jrm build/main.zip build/main

Now that we have our zip file, navigate to the AWS Lambda console, and open up the Lambda function. Under Code source, click Upload from then choose Zip. Select main.zip from your local source.

What is our state?

We have now completed our Lambda function. Instead of testing the Lambda function through the console, let's use the AWS command line. Use the AWS CLI code snippet below, replacing the instance IDs from your own console.

aws lambda invoke \
    --function-name ec2-status-check \
    --cli-binary-format raw-in-base64-out \
    --payload '{ "InstanceID":["i-082d669441071928e", "i-020e6fbdf269208ff" ]}' \
    response.json

When you run the command, you should see a successful status code as output:

{
    "StatusCode": 200,
    "ExecutedVersion": "$LATEST"
}

With a successful status return, we can inspect the output file, response.json. From your command prompt, type cat response.json.

Inspecting the reponse.json file

Inspecting the reponse.json file

From viewing the response.json file, our instance IDs have the running state right next to them.

Summary

We have covered a fair amount in this working example. AWS Lambda is a very powerful tool and opens up a lot of cost-effective possibilities. Go is a great programming language for working with Lambda. We can do all our development in the local environment and then compile and simply upload a single binary file to AWS Lambda. You can find my demo code on GitHub.

5 Comments
  1. A 9 months ago

    incomplete code snippet

  2. Patrick 7 months ago

    I followed the tutorial, and I got following error

    {
    “StatusCode”: 200,
    “FunctionError”: “Unhandled”,
    “ExecutedVersion”: “$LATEST”
    }

    when I cat response.json

    “errorMessage”:”operation error EC2: DescribeInstances, https response error StatusCode: 400, RequestID: 258417a3-8949-42de-aa07-3c3a6379e0ba, api error InvalidInstanceID.NotFound: The instance IDs ‘i-0da57488ea6c2b981, i-0f17ee8d4fbf523c2’ do not exist”,”errorType”:”OperationError”}

    I’m very sure the instance IDs are correct and their state is ‘stopped’ . I copy and pasted the instance id

  3. Aphsa 4 weeks ago

    Why use “github.com/aws/aws-lambda-go/lambda” when you can have “github.com/aws/aws-sdk-go-v2/service/lambda”

Leave a reply

Please enclose code in pre tags

Your email address will not be published.

*

© 4sysops 2006 - 2023

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account