Blog

Step-by-step Guide: Deploying Multiple AWS Lambda Functions with Pulumi

28.03.2024
Reading time: 9 mins.
Last Updated: 28.03.2024

Table of Contents

This guide will walk you through deploying multiple AWS Lambda functions using Pulumi, an infrastructure as code tool that allows you to define and manage cloud resources using familiar programming languages like JavaScript, TypeScript, Python, Go, and more.

Before you begin, ensure you have the following prerequisites installed:

  1. Pulumi CLI: Install the Pulumi CLI by following the instructions on the official Pulumi website.
  2. AWS Account: You’ll need an AWS account with appropriate permissions to create Lambda functions, IAM roles, and other required resources.
  3. Programming Language: Choose a programming language supported by Pulumi. In this example, we’ll use TypeScript, but you can use JavaScript, Python, Go, etc.

Configuring a local AWS profile allows you to manage multiple AWS credentials on your local machine, which is particularly useful when working with different AWS accounts or roles. Here’s how you can configure a local AWS profile:

• If you haven’t already installed the AWS Command Line Interface (CLI), you can do so by following the instructions on the AWS CLI documentation.
• Run the following command in your terminal:

aws configure --profile <profile-name>

Replace with the desired name for your profile. For example, when prompted, enter the following information:

AWS Access Key ID: This is your AWS Access Key ID, which you can obtain from the AWS Management Console under IAM (Identity and Access Management) > Users > Security credentials.
AWS Secret Access Key: This is your AWS Secret Access Key, which is provided when you generate an access key in the AWS Management Console.
Specify Default Region and Output Format

You’ll also be prompted to specify the default region and output format. These are optional, but it’s recommended to set them for convenience.

Default region name: Enter the AWS region you prefer to use, such as eu-west-1.
Default output format: You can choose the output format, such as json.
Confirm Configuration – review the configuration details displayed on the terminal and confirm that they are correct. Once confirmed, the AWS profile will be configured locally on your machine.
Verify Configuration – to verify that your profile has been successfully configured, you can check the AWS CLI configuration files stored on your local machine.
– On Linux or macOS, check the ~/.aws/config and ~/.aws/credentials files.
– On Windows, check the C:\Users\USERNAME.aws\config and C:\Users\USERNAME.aws\credentials files.

To create a private S3 bucket using the AWS Command Line Interface (CLI), you can utilize the AWS s3api create-bucket command along with specifying the appropriate ACL (Access Control List) settings. Here’s how to create a private bucket:

aws s3api create-bucket --bucket --region --profile --acl private

Replace with the desired name for your S3 bucket and with the AWS region where you want to create the bucket. This command creates an S3 bucket with the specified name in the specified region, and sets its ACL to private, meaning only the bucket owner will have access to it by default.

Create a new directory and Clone our official repository https://github.com/joto2itgix/lambda_deploy. And initialize the Pulumi project:

mkdir lambda_deploy
cd lambda_deploy
git clone
npm install
npm install @pulumi/archive

Open the index.ts file in your preferred editor

import * as aws from "@pulumi/aws";
import * as ELEMENTS from "./elements"

(async () => {
var clientName = "lambdas"
var env = clientName+"-dev"
var CONFIG: any;
CONFIG = await ELEMENTS.readFile(env+".yaml");

const vpc_main = ELEMENTS.createVPC(env, CONFIG["vpc"]["name"], CONFIG["vpc"]["cidr"], CONFIG["vpc"]["tenancy"], CONFIG["default_tags"])

const gw1 = ELEMENTS.createGW(env, "gw", vpc_main, CONFIG["default_tags"])

const subnets:any = []
CONFIG["subnets"].forEach(function (subnet: any) {
subnets.push(
ELEMENTS.createSubnet (env, subnet["name"], vpc_main, subnet["cidr"], CONFIG["region"], subnet["zone"], gw1, CONFIG["default_tags"])
)
});

var ingress_public = [
{ protocol: "tcp", fromPort: 80, toPort: 80, cidrBlocks: ["0.0.0.0/0"] },
{ protocol: "tcp", fromPort: 443, toPort: 443, cidrBlocks: ["0.0.0.0/0"] },
]
var egress = [
{ protocol: "-1", fromPort: 0, toPort: 0, cidrBlocks: ["0.0.0.0/0"] },
]
const webSecurityGroup = ELEMENTS.createNSG (env, "-sg-web", vpc_main, ingress_public, egress)

//SECRETS
const secretManager1 = new aws.secretsmanager.Secret(env+"-sm", {
forceOverwriteReplicaSecret: false,
});
// Store a new secret version
const secretVersion1 = new aws.secretsmanager.SecretVersion(env+"-secrets", {
secretId: secretManager1.id,
secretString: '{"test_var1":"test_valu1","test_valu2":"asd","test_var3":"test_valu3"}',
});
const secretManagerEndpoint = new aws.ec2.VpcEndpoint(env+"-sm-endpoint", {
vpcId: vpc_main.id,
serviceName: "com.amazonaws."+CONFIG["region"]+".secretsmanager",
vpcEndpointType: "Interface",
privateDnsEnabled: true,
subnetIds: subnets,
securityGroupIds: [],
});

//Hello World Lambda
var recipe = {
src: "helloworld",
runtime: "python3.12",
handler: "lambda_handler"
}
var actions = [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface"
]
const lambdaHW = ELEMENTS.createLambda(env, "hw-py", recipe, actions, [subnets[0].id], [webSecurityGroup.id], {foo: "bar"})
const lambdaHWDistribution = ELEMENTS.createCF(env, "hw-py", lambdaHW.functionUrl)

//Print env Lambda
var recipe = {
src: "printenv",
runtime: "python3.12",
handler: "lambda_handler"
}
var actions = [
"ec2:CreateNetworkInterface",
"ec2:DescribeNetworkInterfaces",
"ec2:DeleteNetworkInterface"
]
const lambdaPE = ELEMENTS.createLambda(env, "pe-py", recipe, actions, [subnets[0].id], [webSecurityGroup.id], {foo2: "bar2"})
})()

This code utilizes the Pulumi infrastructure as code (IaC) library to define and deploy AWS resources. Let’s break down the main components and actions taking place in the script:

• Imports: The script imports necessary modules from Pulumi’s @pulumi/aws library and a custom module ./elements.
• Asynchronous Function: The script defines an asynchronous function using an immediately invoked function expression (IIFE). This function appears to be the main entry point for deploying AWS resources.
• Environment Configuration: Loads environment-specific configuration from YAML files using the ELEMENTS.readFile function.
• Creation of VPC and Subnets: The script creates a Virtual Private Cloud (VPC) and associated subnets using the ELEMENTS.createVPC and ELEMENTS.createSubnet functions respectively.
• Network Security Groups: It creates a Network Security Group (NSG) for web traffic (webSecurityGroup).
• Secrets Management: Secret Manager resources are created (secretManager1, secretVersion1, secretManagerEndpoint) for managing secrets in AWS Secrets Manager.
• Lambda Functions: The script defines two Lambda functions (lambdaHW, lambdaPE) using the ELEMENTS.createLambda function. Each Lambda function is associated with specific actions and security groups.
• CloudFront: It creates a CloudFront distribution (lambdaHWDistribution) for the Lambda function lambdaHW.
• Script Execution: The script executes immediately when run.

In this example, the lambda functions are simple and separate python files. But with ease can be extended to complex, or ones with different runtime.

Run the following command to deploy your Pulumi stack:

pulumi login 's3://itgix-lambdas-pulumi-state?region=eu-west-1&profile=itgix'

export PULUMI_CONFIG_PASSPHRASE=
pulumi stack select lambdas --create --non-interactive
pulumi config set aws:region eu-west-1
pulumi config set aws:profile itgix

pulumi preview

pulumi up --yes --non-interactive

pulumi login: This command is used to authenticate with the Pulumi service. It specifies a state storage location (s3://itgix-lambdas-pulumi-state?region=eu-west-1&profile=itgix) where the state of the Pulumi stack will be stored. The –region and –profile options specify the AWS region and AWS profile to use for authentication, respectively.
export PULUMI_CONFIG_PASSPHRASE=: This command exports an empty passphrase to the PULUMI_CONFIG_PASSPHRASE environment variable. This variable is used for encrypting sensitive configuration values in the Pulumi stack.
pulumi stack select lambdas –create –non-interactive: This command selects the Pulumi stack named lambdas. If the stack does not exist, it will be created (–create). The –non-interactive option specifies that the command should not prompt for user input, which is useful for automation.
pulumi config set aws:region eu-west-1: This command sets the AWS region configuration for the selected Pulumi stack to eu-west-1. This region will be used for deploying AWS resources.
pulumi config set aws:profile itgix: This command sets the AWS profile configuration for the selected Pulumi stack to itgix. This profile will be used for authentication when interacting with AWS services.
pulumi preview: This command displays a preview of the changes that will be applied to the infrastructure. It shows the resources that will be created, updated, or deleted based on the current Pulumi program.
pulumi up –yes –non-interactive: This command applies the changes to the infrastructure. The –yes option automatically approves and applies the changes without asking for confirmation. The –non-interactive option ensures that the command does not prompt for user input.

Overall, this script logs in to the Pulumi service, selects or creates a Pulumi stack, configures AWS region and profile settings, previews the infrastructure changes, and applies the changes to deploy the AWS resources defined in the Pulumi program.

The output will look something like this.
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> pulumi login 's3://itgix-lambdas-pulumi-state?region=eu-west-1&profile=itgix'
Logged in to localhost.localdomain as vladimir.dimitrov (s3://itgix-lambdas-pulumi-state?region=eu-west-1&profile=itgix)
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy>
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> export PULUMI_CONFIG_PASSPHRASE=
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> pulumi stack select lambdas --create --non-interactive
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> pulumi config set aws:region eu-west-1
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> pulumi config set aws:profile itgix
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy>
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> pulumi preview
Previewing update (lambdas):
Type Name Plan

pulumi:pulumi:Stack lambdas-lambdas create

├─ aws:iam:Policy lambdas-dev-pe-py-policy create

├─ aws:iam:Policy lambdas-dev-hw-py-policy create

├─ aws:secretsmanager:Secret lambdas-dev-sm create

├─ aws:ec2:Vpc lambdas-dev-vpc create

├─ aws:secretsmanager:SecretVersion lambdas-dev-secrets create

├─ aws:iam:Role lambdas-dev-pe-py-iam create

├─ aws:iam:Role lambdas-dev-hw-py-iam create

├─ aws:ec2:RouteTable lambdas-dev-rt1 create

├─ aws:ec2:Subnet lambdas-dev-subnet create

├─ aws:ec2:InternetGateway lambdas-dev-gw create

├─ aws:ec2:SecurityGroup lambdas-dev-sg-web create

├─ aws:ec2:VpcEndpoint lambdas-dev-sm-endpoint create

├─ aws:ec2:RouteTableAssociation lambdas-dev-rt1a create

├─ aws:lambda:Function lambdas-dev-pe-py-printenv create

├─ aws:lambda:FunctionUrl lambdas-dev-pe-py-url create

├─ aws:lambda:Function lambdas-dev-hw-py-helloworld create

└─ aws:lambda:FunctionUrl lambdas-dev-hw-py-url create

Resources:

18 to create

vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy>
vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> pulumi up --yes --non-interactive
Previewing update (lambdas):

pulumi:pulumi:Stack lambdas-lambdas create
@ previewing update….

aws:secretsmanager:Secret lambdas-dev-sm create

aws:iam:Policy lambdas-dev-hw-py-policy create

aws:ec2:Vpc lambdas-dev-vpc create

aws:iam:Policy lambdas-dev-pe-py-policy create

aws:secretsmanager:SecretVersion lambdas-dev-secrets create

aws:iam:Role lambdas-dev-hw-py-iam create

aws:iam:Role lambdas-dev-pe-py-iam create

aws:ec2:Subnet lambdas-dev-subnet create

aws:ec2:InternetGateway lambdas-dev-gw create

aws:ec2:SecurityGroup lambdas-dev-sg-web create

aws:ec2:RouteTable lambdas-dev-rt1 create

aws:ec2:VpcEndpoint lambdas-dev-sm-endpoint create

aws:lambda:Function lambdas-dev-pe-py-printenv create

aws:lambda:Function lambdas-dev-hw-py-helloworld create

aws:ec2:RouteTableAssociation lambdas-dev-rt1a create

aws:lambda:FunctionUrl lambdas-dev-pe-py-url create

aws:lambda:FunctionUrl lambdas-dev-hw-py-url create

pulumi:pulumi:Stack lambdas-lambdas create
Resources:

18 to create

Updating (lambdas):

pulumi:pulumi:Stack lambdas-lambdas creating (0s)
@ updating……

aws:iam:Policy lambdas-dev-pe-py-policy creating (0s)
@ updating….

aws:iam:Policy lambdas-dev-pe-py-policy created (1s)

aws:iam:Policy lambdas-dev-hw-py-policy creating (0s)

aws:secretsmanager:Secret lambdas-dev-sm creating (0s)

aws:ec2:Vpc lambdas-dev-vpc creating (0s)

aws:iam:Role lambdas-dev-pe-py-iam creating (0s)
@ updating….

aws:iam:Policy lambdas-dev-hw-py-policy created (1s)
@ updating….

aws:secretsmanager:Secret lambdas-dev-sm created (1s)

aws:iam:Role lambdas-dev-hw-py-iam creating (0s)
@ updating….

aws:iam:Role lambdas-dev-pe-py-iam created (2s)

aws:secretsmanager:SecretVersion lambdas-dev-secrets creating (0s)
@ updating….

aws:iam:Role lambdas-dev-hw-py-iam created (1s)
@ updating….

aws:secretsmanager:SecretVersion lambdas-dev-secrets created (1s)
@ updating…………

aws:ec2:Vpc lambdas-dev-vpc created (13s)

aws:ec2:InternetGateway lambdas-dev-gw creating (0s)

aws:ec2:Subnet lambdas-dev-subnet creating (0s)

aws:ec2:SecurityGroup lambdas-dev-sg-web creating (0s)
@ updating…..

aws:ec2:InternetGateway lambdas-dev-gw created (1s)

aws:ec2:Subnet lambdas-dev-subnet created (1s)

aws:ec2:RouteTable lambdas-dev-rt1 creating (0s)
@ updating….

aws:ec2:VpcEndpoint lambdas-dev-sm-endpoint creating (0s)
@ updating….

aws:ec2:SecurityGroup lambdas-dev-sg-web created (3s)

aws:ec2:RouteTable lambdas-dev-rt1 created (2s)
@ updating….

aws:lambda:Function lambdas-dev-hw-py-helloworld creating (0s)

aws:lambda:Function lambdas-dev-pe-py-printenv creating (0s)

aws:ec2:RouteTableAssociation lambdas-dev-rt1a creating (0s)
@ updating…..

aws:ec2:RouteTableAssociation lambdas-dev-rt1a created (1s)
@ updating…………………………………..

aws:ec2:VpcEndpoint lambdas-dev-sm-endpoint created (42s)
@ updating…………………………………………………………………………………………………………………………………………

aws:lambda:Function lambdas-dev-hw-py-helloworld created (192s)

aws:lambda:FunctionUrl lambdas-dev-hw-py-url creating (0s)
@ updating….

aws:lambda:FunctionUrl lambdas-dev-hw-py-url created (1s)
@ updating….

aws:cloudfront:Distribution lambdas-dev-hw-py-cf creating (0s)
@ updating…….

aws:lambda:Function lambdas-dev-pe-py-printenv created (199s)
@ updating….

aws:lambda:FunctionUrl lambdas-dev-pe-py-url creating (0s)
@ updating….

aws:lambda:FunctionUrl lambdas-dev-pe-py-url created (0.93s)
@ updating……………………………………………………………………………………………………………………………………………………………………………….

aws:cloudfront:Distribution lambdas-dev-hw-py-cf created (201s)
@ updating….

pulumi:pulumi:Stack lambdas-lambdas created (1s)
Resources:

19 created

Duration: 7m2s

After deployment is complete, you can test your Lambda functions via the AWS Management Console, AWS CLI, or by invoking them pro-grammatically.

Lambda functions via the AWS Management Console

We will test our two lambda function through their URLs. The URL can be find inside the lambda itself.

two lambda function

First one is returning simple hello world text.

two lambda function, hello text

With the same code we created a CloudFront Distribution attached to the lambda. So, we can access it through there too.

CloudFront Distribution attached to the lambda

Running the CF Domain will give us the same result, as the request is forwarded to the lambda URL.

the lambda URL

The second lambda is simple too. But this time is configured to read an environment variable and to print it as an output, instead of the hello world text.

The second lambda is simple too

Updating and deploying the lambda code is simpler than it sounds.
Open any lambda script, update it, and save the code. For our example we will change the hello world text from “Hello from Lambda!” to “I just made an update!”

import json

def lambda_handler(event, context):

#TODO implement

return {
'statusCode': 200,
'body': json.dumps('I just made an update!')
}

Then run:

pulumi preview

pulumi up --yes --non-interactive

Lambda is updated in less than 10sec. and the output looks something like this. You can see that Pulumi is detecting changes inside the lambda code and is triggering the update.

vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> pulumi preview
Previewing update (lambdas):
Type Name Plan Info
pulumi:pulumi:Stack lambdas-lambdas
~ └─ aws:lambda:Function lambdas-dev-hw-py-helloworld update [diff: ~code]

Resources:
~ 1 to update
17 unchanged

vladimir.dimitrov@localhost:~/github/PROJECTS/lambda_deploy> pulumi up --yes --non-interactive
Previewing update (lambdas):

pulumi:pulumi:Stack lambdas-lambdas running
@ previewing update….
.
.
.
Resources:
~ 1 updated
17 unchanged

Duration: 9s

i just made an update

Congratulations! You’ve successfully deployed multiple AWS Lambda functions using Pulumi. You can extend this setup to include additional resources, configure event triggers, and manage your entire infrastructure as code. For further customization and advanced features, refer to the Pulumi documentation.

Leave a Reply

Your email address will not be published. Required fields are marked *

More Posts

Introduction Integrating Alertmanager with Microsoft Teams enables you to receive alerts directly in Teams channels, facilitating swift collaboration with your team to address issues promptly. This guide will use Prometheus-MSTeams,...
Reading
Managing Terraform locals efficiently is crucial for creating clean, maintainable, and reusable configurations. This guide will cover what Terraform locals are, how to implement them, and best practices for their...
Reading
Get In Touch
ITGix provides you with expert consultancy and tailored DevOps services to accelerate your business growth.
Newsletter for
Tech Experts
Join 12,000+ business leaders and engineers who receive blogs, e-Books, and case studies on emerging technology.