Create and Configure AWS Lambda Functions

The aws:lambda/function:Function resource, part of the Pulumi AWS provider, defines a Lambda function and its execution environment: code package, runtime, handler, and optional VPC placement. This guide focuses on four capabilities: Lambda Layers for shared dependencies, VPC networking for private resource access, EFS mounts for persistent storage, and structured logging and error handling.

Lambda functions require IAM execution roles and may reference VPC subnets, security groups, EFS file systems, or CloudWatch Log Groups that must exist separately. The examples are intentionally small. Combine them with your own IAM roles, triggers, and networking infrastructure.

Share code across functions with Lambda Layers

Teams building multiple functions often extract common dependencies into Lambda Layers to reduce deployment package size and share code across functions.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// Common dependencies layer
const example = new aws.lambda.LayerVersion("example", {
    code: new pulumi.asset.FileArchive("layer.zip"),
    layerName: "example_dependencies_layer",
    description: "Common dependencies for Lambda functions",
    compatibleRuntimes: [
        "nodejs20.x",
        "python3.12",
    ],
    compatibleArchitectures: [
        "x86_64",
        "arm64",
    ],
});
// Function using the layer
const exampleFunction = new aws.lambda.Function("example", {
    code: new pulumi.asset.FileArchive("function.zip"),
    name: "example_layered_function",
    role: exampleAwsIamRole.arn,
    handler: "index.handler",
    runtime: aws.lambda.Runtime.NodeJS20dX,
    layers: [example.arn],
    tracingConfig: {
        mode: "Active",
    },
});
import pulumi
import pulumi_aws as aws

# Common dependencies layer
example = aws.lambda_.LayerVersion("example",
    code=pulumi.FileArchive("layer.zip"),
    layer_name="example_dependencies_layer",
    description="Common dependencies for Lambda functions",
    compatible_runtimes=[
        "nodejs20.x",
        "python3.12",
    ],
    compatible_architectures=[
        "x86_64",
        "arm64",
    ])
# Function using the layer
example_function = aws.lambda_.Function("example",
    code=pulumi.FileArchive("function.zip"),
    name="example_layered_function",
    role=example_aws_iam_role["arn"],
    handler="index.handler",
    runtime=aws.lambda_.Runtime.NODE_JS20D_X,
    layers=[example.arn],
    tracing_config={
        "mode": "Active",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Common dependencies layer
		example, err := lambda.NewLayerVersion(ctx, "example", &lambda.LayerVersionArgs{
			Code:        pulumi.NewFileArchive("layer.zip"),
			LayerName:   pulumi.String("example_dependencies_layer"),
			Description: pulumi.String("Common dependencies for Lambda functions"),
			CompatibleRuntimes: pulumi.StringArray{
				pulumi.String("nodejs20.x"),
				pulumi.String("python3.12"),
			},
			CompatibleArchitectures: pulumi.StringArray{
				pulumi.String("x86_64"),
				pulumi.String("arm64"),
			},
		})
		if err != nil {
			return err
		}
		// Function using the layer
		_, err = lambda.NewFunction(ctx, "example", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("function.zip"),
			Name:    pulumi.String("example_layered_function"),
			Role:    pulumi.Any(exampleAwsIamRole.Arn),
			Handler: pulumi.String("index.handler"),
			Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
			Layers: pulumi.StringArray{
				example.Arn,
			},
			TracingConfig: &lambda.FunctionTracingConfigArgs{
				Mode: pulumi.String("Active"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    // Common dependencies layer
    var example = new Aws.Lambda.LayerVersion("example", new()
    {
        Code = new FileArchive("layer.zip"),
        LayerName = "example_dependencies_layer",
        Description = "Common dependencies for Lambda functions",
        CompatibleRuntimes = new[]
        {
            "nodejs20.x",
            "python3.12",
        },
        CompatibleArchitectures = new[]
        {
            "x86_64",
            "arm64",
        },
    });

    // Function using the layer
    var exampleFunction = new Aws.Lambda.Function("example", new()
    {
        Code = new FileArchive("function.zip"),
        Name = "example_layered_function",
        Role = exampleAwsIamRole.Arn,
        Handler = "index.handler",
        Runtime = Aws.Lambda.Runtime.NodeJS20dX,
        Layers = new[]
        {
            example.Arn,
        },
        TracingConfig = new Aws.Lambda.Inputs.FunctionTracingConfigArgs
        {
            Mode = "Active",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lambda.LayerVersion;
import com.pulumi.aws.lambda.LayerVersionArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.inputs.FunctionTracingConfigArgs;
import com.pulumi.asset.FileArchive;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        // Common dependencies layer
        var example = new LayerVersion("example", LayerVersionArgs.builder()
            .code(new FileArchive("layer.zip"))
            .layerName("example_dependencies_layer")
            .description("Common dependencies for Lambda functions")
            .compatibleRuntimes(            
                "nodejs20.x",
                "python3.12")
            .compatibleArchitectures(            
                "x86_64",
                "arm64")
            .build());

        // Function using the layer
        var exampleFunction = new Function("exampleFunction", FunctionArgs.builder()
            .code(new FileArchive("function.zip"))
            .name("example_layered_function")
            .role(exampleAwsIamRole.arn())
            .handler("index.handler")
            .runtime("nodejs20.x")
            .layers(example.arn())
            .tracingConfig(FunctionTracingConfigArgs.builder()
                .mode("Active")
                .build())
            .build());

    }
}
resources:
  # Common dependencies layer
  example:
    type: aws:lambda:LayerVersion
    properties:
      code:
        fn::FileArchive: layer.zip
      layerName: example_dependencies_layer
      description: Common dependencies for Lambda functions
      compatibleRuntimes:
        - nodejs20.x
        - python3.12
      compatibleArchitectures:
        - x86_64
        - arm64
  # Function using the layer
  exampleFunction:
    type: aws:lambda:Function
    name: example
    properties:
      code:
        fn::FileArchive: function.zip
      name: example_layered_function
      role: ${exampleAwsIamRole.arn}
      handler: index.handler
      runtime: nodejs20.x
      layers:
        - ${example.arn}
      tracingConfig:
        mode: Active

When a function includes layers, Lambda loads them into /opt at runtime, making libraries available without bundling them in every deployment package. The layers property accepts up to 5 Layer Version ARNs. The compatibleRuntimes property on the Layer ensures it works with your function’s runtime. The tracingConfig enables X-Ray distributed tracing for observability.

Connect to private resources in a VPC

Functions that access RDS databases or internal APIs need VPC configuration to join private subnets and communicate with resources that aren’t publicly accessible.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.lambda.Function("example", {
    code: new pulumi.asset.FileArchive("function.zip"),
    name: "example_vpc_function",
    role: exampleAwsIamRole.arn,
    handler: "app.handler",
    runtime: aws.lambda.Runtime.Python3d12,
    memorySize: 1024,
    timeout: 30,
    vpcConfig: {
        subnetIds: [
            examplePrivate1.id,
            examplePrivate2.id,
        ],
        securityGroupIds: [exampleLambda.id],
        ipv6AllowedForDualStack: true,
    },
    ephemeralStorage: {
        size: 5120,
    },
    snapStart: {
        applyOn: "PublishedVersions",
    },
});
import pulumi
import pulumi_aws as aws

example = aws.lambda_.Function("example",
    code=pulumi.FileArchive("function.zip"),
    name="example_vpc_function",
    role=example_aws_iam_role["arn"],
    handler="app.handler",
    runtime=aws.lambda_.Runtime.PYTHON3D12,
    memory_size=1024,
    timeout=30,
    vpc_config={
        "subnet_ids": [
            example_private1["id"],
            example_private2["id"],
        ],
        "security_group_ids": [example_lambda["id"]],
        "ipv6_allowed_for_dual_stack": True,
    },
    ephemeral_storage={
        "size": 5120,
    },
    snap_start={
        "apply_on": "PublishedVersions",
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_, err := lambda.NewFunction(ctx, "example", &lambda.FunctionArgs{
			Code:       pulumi.NewFileArchive("function.zip"),
			Name:       pulumi.String("example_vpc_function"),
			Role:       pulumi.Any(exampleAwsIamRole.Arn),
			Handler:    pulumi.String("app.handler"),
			Runtime:    pulumi.String(lambda.RuntimePython3d12),
			MemorySize: pulumi.Int(1024),
			Timeout:    pulumi.Int(30),
			VpcConfig: &lambda.FunctionVpcConfigArgs{
				SubnetIds: pulumi.StringArray{
					examplePrivate1.Id,
					examplePrivate2.Id,
				},
				SecurityGroupIds: pulumi.StringArray{
					exampleLambda.Id,
				},
				Ipv6AllowedForDualStack: pulumi.Bool(true),
			},
			EphemeralStorage: &lambda.FunctionEphemeralStorageArgs{
				Size: pulumi.Int(5120),
			},
			SnapStart: &lambda.FunctionSnapStartArgs{
				ApplyOn: pulumi.String("PublishedVersions"),
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.Lambda.Function("example", new()
    {
        Code = new FileArchive("function.zip"),
        Name = "example_vpc_function",
        Role = exampleAwsIamRole.Arn,
        Handler = "app.handler",
        Runtime = Aws.Lambda.Runtime.Python3d12,
        MemorySize = 1024,
        Timeout = 30,
        VpcConfig = new Aws.Lambda.Inputs.FunctionVpcConfigArgs
        {
            SubnetIds = new[]
            {
                examplePrivate1.Id,
                examplePrivate2.Id,
            },
            SecurityGroupIds = new[]
            {
                exampleLambda.Id,
            },
            Ipv6AllowedForDualStack = true,
        },
        EphemeralStorage = new Aws.Lambda.Inputs.FunctionEphemeralStorageArgs
        {
            Size = 5120,
        },
        SnapStart = new Aws.Lambda.Inputs.FunctionSnapStartArgs
        {
            ApplyOn = "PublishedVersions",
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.inputs.FunctionVpcConfigArgs;
import com.pulumi.aws.lambda.inputs.FunctionEphemeralStorageArgs;
import com.pulumi.aws.lambda.inputs.FunctionSnapStartArgs;
import com.pulumi.asset.FileArchive;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new Function("example", FunctionArgs.builder()
            .code(new FileArchive("function.zip"))
            .name("example_vpc_function")
            .role(exampleAwsIamRole.arn())
            .handler("app.handler")
            .runtime("python3.12")
            .memorySize(1024)
            .timeout(30)
            .vpcConfig(FunctionVpcConfigArgs.builder()
                .subnetIds(                
                    examplePrivate1.id(),
                    examplePrivate2.id())
                .securityGroupIds(exampleLambda.id())
                .ipv6AllowedForDualStack(true)
                .build())
            .ephemeralStorage(FunctionEphemeralStorageArgs.builder()
                .size(5120)
                .build())
            .snapStart(FunctionSnapStartArgs.builder()
                .applyOn("PublishedVersions")
                .build())
            .build());

    }
}
resources:
  example:
    type: aws:lambda:Function
    properties:
      code:
        fn::FileArchive: function.zip
      name: example_vpc_function
      role: ${exampleAwsIamRole.arn}
      handler: app.handler
      runtime: python3.12
      memorySize: 1024
      timeout: 30
      vpcConfig:
        subnetIds:
          - ${examplePrivate1.id}
          - ${examplePrivate2.id}
        securityGroupIds:
          - ${exampleLambda.id}
        ipv6AllowedForDualStack: true
      ephemeralStorage:
        size: 5120
      snapStart:
        applyOn: PublishedVersions

The vpcConfig property places your function in specified subnets with attached security groups, enabling access to RDS and internal services. Lambda manages the network interfaces automatically. The ipv6AllowedForDualStack property enables IPv6 connectivity. The ephemeralStorage property increases temporary storage beyond the default 512MB. The snapStart property reduces cold start latency by pre-initializing the function.

Mount EFS for persistent storage across invocations

Workloads that need persistent storage beyond the 10GB ephemeral limit can mount EFS file systems, enabling functions to share state or access large datasets.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// EFS file system for Lambda
const example = new aws.efs.FileSystem("example", {
    encrypted: true,
    tags: {
        Name: "lambda-efs",
    },
});
const config = new pulumi.Config();
// List of subnet IDs for EFS mount targets
const subnetIds = config.getObject<Array<string>>("subnetIds") || [
    "subnet-12345678",
    "subnet-87654321",
];
// Mount target in each subnet
const exampleMountTarget: aws.efs.MountTarget[] = [];
for (const range = {value: 0}; range.value < subnetIds.length; range.value++) {
    exampleMountTarget.push(new aws.efs.MountTarget(`example-${range.value}`, {
        fileSystemId: example.id,
        subnetId: subnetIds[range.value],
        securityGroups: [efs.id],
    }));
}
// Access point for Lambda
const exampleAccessPoint = new aws.efs.AccessPoint("example", {
    fileSystemId: example.id,
    rootDirectory: {
        path: "/lambda",
        creationInfo: {
            ownerGid: 1000,
            ownerUid: 1000,
            permissions: "755",
        },
    },
    posixUser: {
        gid: 1000,
        uid: 1000,
    },
});
// Lambda function with EFS
const exampleFunction = new aws.lambda.Function("example", {
    code: new pulumi.asset.FileArchive("function.zip"),
    name: "example_efs_function",
    role: exampleAwsIamRole.arn,
    handler: "index.handler",
    runtime: aws.lambda.Runtime.NodeJS20dX,
    vpcConfig: {
        subnetIds: subnetIds,
        securityGroupIds: [lambda.id],
    },
    fileSystemConfig: {
        arn: exampleAccessPoint.arn,
        localMountPath: "/mnt/data",
    },
}, {
    dependsOn: [exampleMountTarget],
});
import pulumi
import pulumi_aws as aws

# EFS file system for Lambda
example = aws.efs.FileSystem("example",
    encrypted=True,
    tags={
        "Name": "lambda-efs",
    })
config = pulumi.Config()
# List of subnet IDs for EFS mount targets
subnet_ids = config.get_object("subnetIds")
if subnet_ids is None:
    subnet_ids = [
        "subnet-12345678",
        "subnet-87654321",
    ]
# Mount target in each subnet
example_mount_target = []
for range in [{"value": i} for i in range(0, len(subnet_ids))]:
    example_mount_target.append(aws.efs.MountTarget(f"example-{range['value']}",
        file_system_id=example.id,
        subnet_id=subnet_ids[range["value"]],
        security_groups=[efs["id"]]))
# Access point for Lambda
example_access_point = aws.efs.AccessPoint("example",
    file_system_id=example.id,
    root_directory={
        "path": "/lambda",
        "creation_info": {
            "owner_gid": 1000,
            "owner_uid": 1000,
            "permissions": "755",
        },
    },
    posix_user={
        "gid": 1000,
        "uid": 1000,
    })
# Lambda function with EFS
example_function = aws.lambda_.Function("example",
    code=pulumi.FileArchive("function.zip"),
    name="example_efs_function",
    role=example_aws_iam_role["arn"],
    handler="index.handler",
    runtime=aws.lambda_.Runtime.NODE_JS20D_X,
    vpc_config={
        "subnet_ids": subnet_ids,
        "security_group_ids": [lambda_["id"]],
    },
    file_system_config={
        "arn": example_access_point.arn,
        "local_mount_path": "/mnt/data",
    },
    opts = pulumi.ResourceOptions(depends_on=[example_mount_target]))
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/efs"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// EFS file system for Lambda
		example, err := efs.NewFileSystem(ctx, "example", &efs.FileSystemArgs{
			Encrypted: pulumi.Bool(true),
			Tags: pulumi.StringMap{
				"Name": pulumi.String("lambda-efs"),
			},
		})
		if err != nil {
			return err
		}
		cfg := config.New(ctx, "")
		// List of subnet IDs for EFS mount targets
		subnetIds := []string{
			"subnet-12345678",
			"subnet-87654321",
		}
		if param := cfg.GetObject("subnetIds"); param != nil {
			subnetIds = param
		}
		// Mount target in each subnet
		var exampleMountTarget []*efs.MountTarget
		for index := 0; index < len(subnetIds); index++ {
			key0 := index
			val0 := index
			__res, err := efs.NewMountTarget(ctx, fmt.Sprintf("example-%v", key0), &efs.MountTargetArgs{
				FileSystemId: example.ID(),
				SubnetId:     pulumi.String(subnetIds[val0]),
				SecurityGroups: pulumi.StringArray{
					efs.Id,
				},
			})
			if err != nil {
				return err
			}
			exampleMountTarget = append(exampleMountTarget, __res)
		}
		// Access point for Lambda
		exampleAccessPoint, err := efs.NewAccessPoint(ctx, "example", &efs.AccessPointArgs{
			FileSystemId: example.ID(),
			RootDirectory: &efs.AccessPointRootDirectoryArgs{
				Path: pulumi.String("/lambda"),
				CreationInfo: &efs.AccessPointRootDirectoryCreationInfoArgs{
					OwnerGid:    pulumi.Int(1000),
					OwnerUid:    pulumi.Int(1000),
					Permissions: pulumi.String("755"),
				},
			},
			PosixUser: &efs.AccessPointPosixUserArgs{
				Gid: pulumi.Int(1000),
				Uid: pulumi.Int(1000),
			},
		})
		if err != nil {
			return err
		}
		// Lambda function with EFS
		_, err = lambda.NewFunction(ctx, "example", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("function.zip"),
			Name:    pulumi.String("example_efs_function"),
			Role:    pulumi.Any(exampleAwsIamRole.Arn),
			Handler: pulumi.String("index.handler"),
			Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
			VpcConfig: &lambda.FunctionVpcConfigArgs{
				SubnetIds: subnetIds,
				SecurityGroupIds: pulumi.StringArray{
					lambda.Id,
				},
			},
			FileSystemConfig: &lambda.FunctionFileSystemConfigArgs{
				Arn:            exampleAccessPoint.Arn,
				LocalMountPath: pulumi.String("/mnt/data"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			exampleMountTarget,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    // EFS file system for Lambda
    var example = new Aws.Efs.FileSystem("example", new()
    {
        Encrypted = true,
        Tags = 
        {
            { "Name", "lambda-efs" },
        },
    });

    var config = new Config();
    // List of subnet IDs for EFS mount targets
    var subnetIds = config.GetObject<string[]>("subnetIds") ?? new[]
    {
        "subnet-12345678",
        "subnet-87654321",
    };
    // Mount target in each subnet
    var exampleMountTarget = new List<Aws.Efs.MountTarget>();
    for (var rangeIndex = 0; rangeIndex < subnetIds.Length; rangeIndex++)
    {
        var range = new { Value = rangeIndex };
        exampleMountTarget.Add(new Aws.Efs.MountTarget($"example-{range.Value}", new()
        {
            FileSystemId = example.Id,
            SubnetId = subnetIds[range.Value],
            SecurityGroups = new[]
            {
                efs.Id,
            },
        }));
    }
    // Access point for Lambda
    var exampleAccessPoint = new Aws.Efs.AccessPoint("example", new()
    {
        FileSystemId = example.Id,
        RootDirectory = new Aws.Efs.Inputs.AccessPointRootDirectoryArgs
        {
            Path = "/lambda",
            CreationInfo = new Aws.Efs.Inputs.AccessPointRootDirectoryCreationInfoArgs
            {
                OwnerGid = 1000,
                OwnerUid = 1000,
                Permissions = "755",
            },
        },
        PosixUser = new Aws.Efs.Inputs.AccessPointPosixUserArgs
        {
            Gid = 1000,
            Uid = 1000,
        },
    });

    // Lambda function with EFS
    var exampleFunction = new Aws.Lambda.Function("example", new()
    {
        Code = new FileArchive("function.zip"),
        Name = "example_efs_function",
        Role = exampleAwsIamRole.Arn,
        Handler = "index.handler",
        Runtime = Aws.Lambda.Runtime.NodeJS20dX,
        VpcConfig = new Aws.Lambda.Inputs.FunctionVpcConfigArgs
        {
            SubnetIds = subnetIds,
            SecurityGroupIds = new[]
            {
                lambda.Id,
            },
        },
        FileSystemConfig = new Aws.Lambda.Inputs.FunctionFileSystemConfigArgs
        {
            Arn = exampleAccessPoint.Arn,
            LocalMountPath = "/mnt/data",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            exampleMountTarget,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.efs.FileSystem;
import com.pulumi.aws.efs.FileSystemArgs;
import com.pulumi.aws.efs.MountTarget;
import com.pulumi.aws.efs.MountTargetArgs;
import com.pulumi.aws.efs.AccessPoint;
import com.pulumi.aws.efs.AccessPointArgs;
import com.pulumi.aws.efs.inputs.AccessPointRootDirectoryArgs;
import com.pulumi.aws.efs.inputs.AccessPointRootDirectoryCreationInfoArgs;
import com.pulumi.aws.efs.inputs.AccessPointPosixUserArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.inputs.FunctionVpcConfigArgs;
import com.pulumi.aws.lambda.inputs.FunctionFileSystemConfigArgs;
import com.pulumi.asset.FileArchive;
import com.pulumi.codegen.internal.KeyedValue;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var config = ctx.config();
        // EFS file system for Lambda
        var example = new FileSystem("example", FileSystemArgs.builder()
            .encrypted(true)
            .tags(Map.of("Name", "lambda-efs"))
            .build());

        final var subnetIds = config.get("subnetIds").orElse(        
            "subnet-12345678",
            "subnet-87654321");
        // Mount target in each subnet
        for (var i = 0; i < subnetIds.length(); i++) {
            new MountTarget("exampleMountTarget-" + i, MountTargetArgs.builder()
                .fileSystemId(example.id())
                .subnetId(subnetIds[range.value()])
                .securityGroups(efs.id())
                .build());

        
}
        // Access point for Lambda
        var exampleAccessPoint = new AccessPoint("exampleAccessPoint", AccessPointArgs.builder()
            .fileSystemId(example.id())
            .rootDirectory(AccessPointRootDirectoryArgs.builder()
                .path("/lambda")
                .creationInfo(AccessPointRootDirectoryCreationInfoArgs.builder()
                    .ownerGid(1000)
                    .ownerUid(1000)
                    .permissions("755")
                    .build())
                .build())
            .posixUser(AccessPointPosixUserArgs.builder()
                .gid(1000)
                .uid(1000)
                .build())
            .build());

        // Lambda function with EFS
        var exampleFunction = new Function("exampleFunction", FunctionArgs.builder()
            .code(new FileArchive("function.zip"))
            .name("example_efs_function")
            .role(exampleAwsIamRole.arn())
            .handler("index.handler")
            .runtime("nodejs20.x")
            .vpcConfig(FunctionVpcConfigArgs.builder()
                .subnetIds(subnetIds)
                .securityGroupIds(lambda.id())
                .build())
            .fileSystemConfig(FunctionFileSystemConfigArgs.builder()
                .arn(exampleAccessPoint.arn())
                .localMountPath("/mnt/data")
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(exampleMountTarget)
                .build());

    }
}

The fileSystemConfig property mounts an EFS access point at the specified localMountPath. At runtime, your function code reads and writes files to /mnt/data, which persists across invocations. The example creates an EFS file system, mount targets in each subnet, and an access point with specific POSIX permissions. The function depends on mount targets being ready before it can successfully mount the file system.

Configure structured logging with JSON format

Production functions benefit from structured logging that makes it easier to query and analyze logs in CloudWatch Logs Insights.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const example = new aws.cloudwatch.LogGroup("example", {
    name: "/aws/lambda/example_function",
    retentionInDays: 14,
    tags: {
        Environment: "production",
        Application: "example",
    },
});
const exampleFunction = new aws.lambda.Function("example", {
    code: new pulumi.asset.FileArchive("function.zip"),
    name: "example_function",
    role: exampleAwsIamRole.arn,
    handler: "index.handler",
    runtime: aws.lambda.Runtime.NodeJS20dX,
    loggingConfig: {
        logFormat: "JSON",
        applicationLogLevel: "INFO",
        systemLogLevel: "WARN",
    },
}, {
    dependsOn: [example],
});
import pulumi
import pulumi_aws as aws

example = aws.cloudwatch.LogGroup("example",
    name="/aws/lambda/example_function",
    retention_in_days=14,
    tags={
        "Environment": "production",
        "Application": "example",
    })
example_function = aws.lambda_.Function("example",
    code=pulumi.FileArchive("function.zip"),
    name="example_function",
    role=example_aws_iam_role["arn"],
    handler="index.handler",
    runtime=aws.lambda_.Runtime.NODE_JS20D_X,
    logging_config={
        "log_format": "JSON",
        "application_log_level": "INFO",
        "system_log_level": "WARN",
    },
    opts = pulumi.ResourceOptions(depends_on=[example]))
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudwatch"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		example, err := cloudwatch.NewLogGroup(ctx, "example", &cloudwatch.LogGroupArgs{
			Name:            pulumi.String("/aws/lambda/example_function"),
			RetentionInDays: pulumi.Int(14),
			Tags: pulumi.StringMap{
				"Environment": pulumi.String("production"),
				"Application": pulumi.String("example"),
			},
		})
		if err != nil {
			return err
		}
		_, err = lambda.NewFunction(ctx, "example", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("function.zip"),
			Name:    pulumi.String("example_function"),
			Role:    pulumi.Any(exampleAwsIamRole.Arn),
			Handler: pulumi.String("index.handler"),
			Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
			LoggingConfig: &lambda.FunctionLoggingConfigArgs{
				LogFormat:           pulumi.String("JSON"),
				ApplicationLogLevel: pulumi.String("INFO"),
				SystemLogLevel:      pulumi.String("WARN"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			example,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.CloudWatch.LogGroup("example", new()
    {
        Name = "/aws/lambda/example_function",
        RetentionInDays = 14,
        Tags = 
        {
            { "Environment", "production" },
            { "Application", "example" },
        },
    });

    var exampleFunction = new Aws.Lambda.Function("example", new()
    {
        Code = new FileArchive("function.zip"),
        Name = "example_function",
        Role = exampleAwsIamRole.Arn,
        Handler = "index.handler",
        Runtime = Aws.Lambda.Runtime.NodeJS20dX,
        LoggingConfig = new Aws.Lambda.Inputs.FunctionLoggingConfigArgs
        {
            LogFormat = "JSON",
            ApplicationLogLevel = "INFO",
            SystemLogLevel = "WARN",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            example,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudwatch.LogGroup;
import com.pulumi.aws.cloudwatch.LogGroupArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.inputs.FunctionLoggingConfigArgs;
import com.pulumi.asset.FileArchive;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        var example = new LogGroup("example", LogGroupArgs.builder()
            .name("/aws/lambda/example_function")
            .retentionInDays(14)
            .tags(Map.ofEntries(
                Map.entry("Environment", "production"),
                Map.entry("Application", "example")
            ))
            .build());

        var exampleFunction = new Function("exampleFunction", FunctionArgs.builder()
            .code(new FileArchive("function.zip"))
            .name("example_function")
            .role(exampleAwsIamRole.arn())
            .handler("index.handler")
            .runtime("nodejs20.x")
            .loggingConfig(FunctionLoggingConfigArgs.builder()
                .logFormat("JSON")
                .applicationLogLevel("INFO")
                .systemLogLevel("WARN")
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(example)
                .build());

    }
}
resources:
  example:
    type: aws:cloudwatch:LogGroup
    properties:
      name: /aws/lambda/example_function
      retentionInDays: 14
      tags:
        Environment: production
        Application: example
  exampleFunction:
    type: aws:lambda:Function
    name: example
    properties:
      code:
        fn::FileArchive: function.zip
      name: example_function
      role: ${exampleAwsIamRole.arn}
      handler: index.handler
      runtime: nodejs20.x
      loggingConfig:
        logFormat: JSON
        applicationLogLevel: INFO
        systemLogLevel: WARN
    options:
      dependsOn:
        - ${example}

The loggingConfig property controls log format and verbosity. Setting logFormat to “JSON” structures logs for easier querying. The applicationLogLevel controls your code’s log output, while systemLogLevel controls Lambda runtime logs. The function depends on the CloudWatch Log Group being created first, which also sets retention policies.

Route failures to dead letter queues

Asynchronous invocations can fail due to throttling, timeouts, or application errors. Dead letter queues capture failed events for later analysis or reprocessing.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

// Main Lambda function
const example = new aws.lambda.Function("example", {
    code: new pulumi.asset.FileArchive("function.zip"),
    name: "example_function",
    role: exampleAwsIamRole.arn,
    handler: "index.handler",
    runtime: aws.lambda.Runtime.NodeJS20dX,
    deadLetterConfig: {
        targetArn: dlq.arn,
    },
});
// Event invoke configuration for retries
const exampleFunctionEventInvokeConfig = new aws.lambda.FunctionEventInvokeConfig("example", {
    functionName: example.name,
    maximumEventAgeInSeconds: 60,
    maximumRetryAttempts: 2,
    destinationConfig: {
        onFailure: {
            destination: dlq.arn,
        },
        onSuccess: {
            destination: success.arn,
        },
    },
});
import pulumi
import pulumi_aws as aws

# Main Lambda function
example = aws.lambda_.Function("example",
    code=pulumi.FileArchive("function.zip"),
    name="example_function",
    role=example_aws_iam_role["arn"],
    handler="index.handler",
    runtime=aws.lambda_.Runtime.NODE_JS20D_X,
    dead_letter_config={
        "target_arn": dlq["arn"],
    })
# Event invoke configuration for retries
example_function_event_invoke_config = aws.lambda_.FunctionEventInvokeConfig("example",
    function_name=example.name,
    maximum_event_age_in_seconds=60,
    maximum_retry_attempts=2,
    destination_config={
        "on_failure": {
            "destination": dlq["arn"],
        },
        "on_success": {
            "destination": success["arn"],
        },
    })
package main

import (
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		// Main Lambda function
		example, err := lambda.NewFunction(ctx, "example", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("function.zip"),
			Name:    pulumi.String("example_function"),
			Role:    pulumi.Any(exampleAwsIamRole.Arn),
			Handler: pulumi.String("index.handler"),
			Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
			DeadLetterConfig: &lambda.FunctionDeadLetterConfigArgs{
				TargetArn: pulumi.Any(dlq.Arn),
			},
		})
		if err != nil {
			return err
		}
		// Event invoke configuration for retries
		_, err = lambda.NewFunctionEventInvokeConfig(ctx, "example", &lambda.FunctionEventInvokeConfigArgs{
			FunctionName:             example.Name,
			MaximumEventAgeInSeconds: pulumi.Int(60),
			MaximumRetryAttempts:     pulumi.Int(2),
			DestinationConfig: &lambda.FunctionEventInvokeConfigDestinationConfigArgs{
				OnFailure: &lambda.FunctionEventInvokeConfigDestinationConfigOnFailureArgs{
					Destination: pulumi.Any(dlq.Arn),
				},
				OnSuccess: &lambda.FunctionEventInvokeConfigDestinationConfigOnSuccessArgs{
					Destination: pulumi.Any(success.Arn),
				},
			},
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    // Main Lambda function
    var example = new Aws.Lambda.Function("example", new()
    {
        Code = new FileArchive("function.zip"),
        Name = "example_function",
        Role = exampleAwsIamRole.Arn,
        Handler = "index.handler",
        Runtime = Aws.Lambda.Runtime.NodeJS20dX,
        DeadLetterConfig = new Aws.Lambda.Inputs.FunctionDeadLetterConfigArgs
        {
            TargetArn = dlq.Arn,
        },
    });

    // Event invoke configuration for retries
    var exampleFunctionEventInvokeConfig = new Aws.Lambda.FunctionEventInvokeConfig("example", new()
    {
        FunctionName = example.Name,
        MaximumEventAgeInSeconds = 60,
        MaximumRetryAttempts = 2,
        DestinationConfig = new Aws.Lambda.Inputs.FunctionEventInvokeConfigDestinationConfigArgs
        {
            OnFailure = new Aws.Lambda.Inputs.FunctionEventInvokeConfigDestinationConfigOnFailureArgs
            {
                Destination = dlq.Arn,
            },
            OnSuccess = new Aws.Lambda.Inputs.FunctionEventInvokeConfigDestinationConfigOnSuccessArgs
            {
                Destination = success.Arn,
            },
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.inputs.FunctionDeadLetterConfigArgs;
import com.pulumi.aws.lambda.FunctionEventInvokeConfig;
import com.pulumi.aws.lambda.FunctionEventInvokeConfigArgs;
import com.pulumi.aws.lambda.inputs.FunctionEventInvokeConfigDestinationConfigArgs;
import com.pulumi.aws.lambda.inputs.FunctionEventInvokeConfigDestinationConfigOnFailureArgs;
import com.pulumi.aws.lambda.inputs.FunctionEventInvokeConfigDestinationConfigOnSuccessArgs;
import com.pulumi.asset.FileArchive;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        // Main Lambda function
        var example = new Function("example", FunctionArgs.builder()
            .code(new FileArchive("function.zip"))
            .name("example_function")
            .role(exampleAwsIamRole.arn())
            .handler("index.handler")
            .runtime("nodejs20.x")
            .deadLetterConfig(FunctionDeadLetterConfigArgs.builder()
                .targetArn(dlq.arn())
                .build())
            .build());

        // Event invoke configuration for retries
        var exampleFunctionEventInvokeConfig = new FunctionEventInvokeConfig("exampleFunctionEventInvokeConfig", FunctionEventInvokeConfigArgs.builder()
            .functionName(example.name())
            .maximumEventAgeInSeconds(60)
            .maximumRetryAttempts(2)
            .destinationConfig(FunctionEventInvokeConfigDestinationConfigArgs.builder()
                .onFailure(FunctionEventInvokeConfigDestinationConfigOnFailureArgs.builder()
                    .destination(dlq.arn())
                    .build())
                .onSuccess(FunctionEventInvokeConfigDestinationConfigOnSuccessArgs.builder()
                    .destination(success.arn())
                    .build())
                .build())
            .build());

    }
}
resources:
  # Main Lambda function
  example:
    type: aws:lambda:Function
    properties:
      code:
        fn::FileArchive: function.zip
      name: example_function
      role: ${exampleAwsIamRole.arn}
      handler: index.handler
      runtime: nodejs20.x
      deadLetterConfig:
        targetArn: ${dlq.arn}
  # Event invoke configuration for retries
  exampleFunctionEventInvokeConfig:
    type: aws:lambda:FunctionEventInvokeConfig
    name: example
    properties:
      functionName: ${example.name}
      maximumEventAgeInSeconds: 60
      maximumRetryAttempts: 2
      destinationConfig:
        onFailure:
          destination: ${dlq.arn}
        onSuccess:
          destination: ${success.arn}

The deadLetterConfig property routes failed invocations to an SQS queue or SNS topic. The FunctionEventInvokeConfig resource controls retry behavior: maximumRetryAttempts sets how many times Lambda retries, and destinationConfig routes successful and failed invocations to different targets. This separation lets you handle failures differently from successes.

Grant CloudWatch Logs permissions to execution role

Lambda functions need explicit IAM permissions to write logs to CloudWatch. Without these permissions, function invocations succeed but logs are silently dropped.

import * as pulumi from "@pulumi/pulumi";
import * as aws from "@pulumi/aws";

const config = new pulumi.Config();
// Name of the Lambda function
const functionName = config.get("functionName") || "example_function";
// CloudWatch Log Group with retention
const example = new aws.cloudwatch.LogGroup("example", {
    name: `/aws/lambda/${functionName}`,
    retentionInDays: 14,
    tags: {
        Environment: "production",
        Function: functionName,
    },
});
// Lambda execution role
const exampleRole = new aws.iam.Role("example", {
    name: "lambda_execution_role",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: "sts:AssumeRole",
            Effect: "Allow",
            Principal: {
                Service: "lambda.amazonaws.com",
            },
        }],
    }),
});
// CloudWatch Logs policy
const lambdaLogging = new aws.iam.Policy("lambda_logging", {
    name: "lambda_logging",
    path: "/",
    description: "IAM policy for logging from Lambda",
    policy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Effect: "Allow",
            Action: [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
            ],
            Resource: ["arn:aws:logs:*:*:*"],
        }],
    }),
});
// Attach logging policy to Lambda role
const lambdaLogs = new aws.iam.RolePolicyAttachment("lambda_logs", {
    role: exampleRole.name,
    policyArn: lambdaLogging.arn,
});
// Lambda function with logging
const exampleFunction = new aws.lambda.Function("example", {
    code: new pulumi.asset.FileArchive("function.zip"),
    name: functionName,
    role: exampleRole.arn,
    handler: "index.handler",
    runtime: aws.lambda.Runtime.NodeJS20dX,
    loggingConfig: {
        logFormat: "JSON",
        applicationLogLevel: "INFO",
        systemLogLevel: "WARN",
    },
}, {
    dependsOn: [
        lambdaLogs,
        example,
    ],
});
import pulumi
import json
import pulumi_aws as aws

config = pulumi.Config()
# Name of the Lambda function
function_name = config.get("functionName")
if function_name is None:
    function_name = "example_function"
# CloudWatch Log Group with retention
example = aws.cloudwatch.LogGroup("example",
    name=f"/aws/lambda/{function_name}",
    retention_in_days=14,
    tags={
        "Environment": "production",
        "Function": function_name,
    })
# Lambda execution role
example_role = aws.iam.Role("example",
    name="lambda_execution_role",
    assume_role_policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Principal": {
                "Service": "lambda.amazonaws.com",
            },
        }],
    }))
# CloudWatch Logs policy
lambda_logging = aws.iam.Policy("lambda_logging",
    name="lambda_logging",
    path="/",
    description="IAM policy for logging from Lambda",
    policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
            ],
            "Resource": ["arn:aws:logs:*:*:*"],
        }],
    }))
# Attach logging policy to Lambda role
lambda_logs = aws.iam.RolePolicyAttachment("lambda_logs",
    role=example_role.name,
    policy_arn=lambda_logging.arn)
# Lambda function with logging
example_function = aws.lambda_.Function("example",
    code=pulumi.FileArchive("function.zip"),
    name=function_name,
    role=example_role.arn,
    handler="index.handler",
    runtime=aws.lambda_.Runtime.NODE_JS20D_X,
    logging_config={
        "log_format": "JSON",
        "application_log_level": "INFO",
        "system_log_level": "WARN",
    },
    opts = pulumi.ResourceOptions(depends_on=[
            lambda_logs,
            example,
        ]))
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/cloudwatch"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/iam"
	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi/config"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		cfg := config.New(ctx, "")
		// Name of the Lambda function
		functionName := "example_function"
		if param := cfg.Get("functionName"); param != "" {
			functionName = param
		}
		// CloudWatch Log Group with retention
		example, err := cloudwatch.NewLogGroup(ctx, "example", &cloudwatch.LogGroupArgs{
			Name:            pulumi.Sprintf("/aws/lambda/%v", functionName),
			RetentionInDays: pulumi.Int(14),
			Tags: pulumi.StringMap{
				"Environment": pulumi.String("production"),
				"Function":    pulumi.String(functionName),
			},
		})
		if err != nil {
			return err
		}
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"Version": "2012-10-17",
			"Statement": []map[string]interface{}{
				map[string]interface{}{
					"Action": "sts:AssumeRole",
					"Effect": "Allow",
					"Principal": map[string]interface{}{
						"Service": "lambda.amazonaws.com",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		// Lambda execution role
		exampleRole, err := iam.NewRole(ctx, "example", &iam.RoleArgs{
			Name:             pulumi.String("lambda_execution_role"),
			AssumeRolePolicy: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		tmpJSON1, err := json.Marshal(map[string]interface{}{
			"Version": "2012-10-17",
			"Statement": []map[string]interface{}{
				map[string]interface{}{
					"Effect": "Allow",
					"Action": []string{
						"logs:CreateLogGroup",
						"logs:CreateLogStream",
						"logs:PutLogEvents",
					},
					"Resource": []string{
						"arn:aws:logs:*:*:*",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json1 := string(tmpJSON1)
		// CloudWatch Logs policy
		lambdaLogging, err := iam.NewPolicy(ctx, "lambda_logging", &iam.PolicyArgs{
			Name:        pulumi.String("lambda_logging"),
			Path:        pulumi.String("/"),
			Description: pulumi.String("IAM policy for logging from Lambda"),
			Policy:      pulumi.String(json1),
		})
		if err != nil {
			return err
		}
		// Attach logging policy to Lambda role
		lambdaLogs, err := iam.NewRolePolicyAttachment(ctx, "lambda_logs", &iam.RolePolicyAttachmentArgs{
			Role:      exampleRole.Name,
			PolicyArn: lambdaLogging.Arn,
		})
		if err != nil {
			return err
		}
		// Lambda function with logging
		_, err = lambda.NewFunction(ctx, "example", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("function.zip"),
			Name:    pulumi.String(functionName),
			Role:    exampleRole.Arn,
			Handler: pulumi.String("index.handler"),
			Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
			LoggingConfig: &lambda.FunctionLoggingConfigArgs{
				LogFormat:           pulumi.String("JSON"),
				ApplicationLogLevel: pulumi.String("INFO"),
				SystemLogLevel:      pulumi.String("WARN"),
			},
		}, pulumi.DependsOn([]pulumi.Resource{
			lambdaLogs,
			example,
		}))
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;

return await Deployment.RunAsync(() => 
{
    var config = new Config();
    // Name of the Lambda function
    var functionName = config.Get("functionName") ?? "example_function";
    // CloudWatch Log Group with retention
    var example = new Aws.CloudWatch.LogGroup("example", new()
    {
        Name = $"/aws/lambda/{functionName}",
        RetentionInDays = 14,
        Tags = 
        {
            { "Environment", "production" },
            { "Function", functionName },
        },
    });

    // Lambda execution role
    var exampleRole = new Aws.Iam.Role("example", new()
    {
        Name = "lambda_execution_role",
        AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Action"] = "sts:AssumeRole",
                    ["Effect"] = "Allow",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "lambda.amazonaws.com",
                    },
                },
            },
        }),
    });

    // CloudWatch Logs policy
    var lambdaLogging = new Aws.Iam.Policy("lambda_logging", new()
    {
        Name = "lambda_logging",
        Path = "/",
        Description = "IAM policy for logging from Lambda",
        PolicyDocument = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Effect"] = "Allow",
                    ["Action"] = new[]
                    {
                        "logs:CreateLogGroup",
                        "logs:CreateLogStream",
                        "logs:PutLogEvents",
                    },
                    ["Resource"] = new[]
                    {
                        "arn:aws:logs:*:*:*",
                    },
                },
            },
        }),
    });

    // Attach logging policy to Lambda role
    var lambdaLogs = new Aws.Iam.RolePolicyAttachment("lambda_logs", new()
    {
        Role = exampleRole.Name,
        PolicyArn = lambdaLogging.Arn,
    });

    // Lambda function with logging
    var exampleFunction = new Aws.Lambda.Function("example", new()
    {
        Code = new FileArchive("function.zip"),
        Name = functionName,
        Role = exampleRole.Arn,
        Handler = "index.handler",
        Runtime = Aws.Lambda.Runtime.NodeJS20dX,
        LoggingConfig = new Aws.Lambda.Inputs.FunctionLoggingConfigArgs
        {
            LogFormat = "JSON",
            ApplicationLogLevel = "INFO",
            SystemLogLevel = "WARN",
        },
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            lambdaLogs,
            example,
        },
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.cloudwatch.LogGroup;
import com.pulumi.aws.cloudwatch.LogGroupArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.iam.Policy;
import com.pulumi.aws.iam.PolicyArgs;
import com.pulumi.aws.iam.RolePolicyAttachment;
import com.pulumi.aws.iam.RolePolicyAttachmentArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.inputs.FunctionLoggingConfigArgs;
import com.pulumi.asset.FileArchive;
import static com.pulumi.codegen.internal.Serialization.*;
import com.pulumi.resources.CustomResourceOptions;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Paths;

public class App {
    public static void main(String[] args) {
        Pulumi.run(App::stack);
    }

    public static void stack(Context ctx) {
        final var config = ctx.config();
        final var functionName = config.get("functionName").orElse("example_function");
        // CloudWatch Log Group with retention
        var example = new LogGroup("example", LogGroupArgs.builder()
            .name(String.format("/aws/lambda/%s", functionName))
            .retentionInDays(14)
            .tags(Map.ofEntries(
                Map.entry("Environment", "production"),
                Map.entry("Function", functionName)
            ))
            .build());

        // Lambda execution role
        var exampleRole = new Role("exampleRole", RoleArgs.builder()
            .name("lambda_execution_role")
            .assumeRolePolicy(serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Statement", jsonArray(jsonObject(
                        jsonProperty("Action", "sts:AssumeRole"),
                        jsonProperty("Effect", "Allow"),
                        jsonProperty("Principal", jsonObject(
                            jsonProperty("Service", "lambda.amazonaws.com")
                        ))
                    )))
                )))
            .build());

        // CloudWatch Logs policy
        var lambdaLogging = new Policy("lambdaLogging", PolicyArgs.builder()
            .name("lambda_logging")
            .path("/")
            .description("IAM policy for logging from Lambda")
            .policy(serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Statement", jsonArray(jsonObject(
                        jsonProperty("Effect", "Allow"),
                        jsonProperty("Action", jsonArray(
                            "logs:CreateLogGroup", 
                            "logs:CreateLogStream", 
                            "logs:PutLogEvents"
                        )),
                        jsonProperty("Resource", jsonArray("arn:aws:logs:*:*:*"))
                    )))
                )))
            .build());

        // Attach logging policy to Lambda role
        var lambdaLogs = new RolePolicyAttachment("lambdaLogs", RolePolicyAttachmentArgs.builder()
            .role(exampleRole.name())
            .policyArn(lambdaLogging.arn())
            .build());

        // Lambda function with logging
        var exampleFunction = new Function("exampleFunction", FunctionArgs.builder()
            .code(new FileArchive("function.zip"))
            .name(functionName)
            .role(exampleRole.arn())
            .handler("index.handler")
            .runtime("nodejs20.x")
            .loggingConfig(FunctionLoggingConfigArgs.builder()
                .logFormat("JSON")
                .applicationLogLevel("INFO")
                .systemLogLevel("WARN")
                .build())
            .build(), CustomResourceOptions.builder()
                .dependsOn(                
                    lambdaLogs,
                    example)
                .build());

    }
}
configuration:
  # Function name variable
  functionName:
    type: string
    default: example_function
resources:
  # CloudWatch Log Group with retention
  example:
    type: aws:cloudwatch:LogGroup
    properties:
      name: /aws/lambda/${functionName}
      retentionInDays: 14
      tags:
        Environment: production
        Function: ${functionName}
  # Lambda execution role
  exampleRole:
    type: aws:iam:Role
    name: example
    properties:
      name: lambda_execution_role
      assumeRolePolicy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Action: sts:AssumeRole
              Effect: Allow
              Principal:
                Service: lambda.amazonaws.com
  # CloudWatch Logs policy
  lambdaLogging:
    type: aws:iam:Policy
    name: lambda_logging
    properties:
      name: lambda_logging
      path: /
      description: IAM policy for logging from Lambda
      policy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Effect: Allow
              Action:
                - logs:CreateLogGroup
                - logs:CreateLogStream
                - logs:PutLogEvents
              Resource:
                - arn:aws:logs:*:*:*
  # Attach logging policy to Lambda role
  lambdaLogs:
    type: aws:iam:RolePolicyAttachment
    name: lambda_logs
    properties:
      role: ${exampleRole.name}
      policyArn: ${lambdaLogging.arn}
  # Lambda function with logging
  exampleFunction:
    type: aws:lambda:Function
    name: example
    properties:
      code:
        fn::FileArchive: function.zip
      name: ${functionName}
      role: ${exampleRole.arn}
      handler: index.handler
      runtime: nodejs20.x
      loggingConfig:
        logFormat: JSON
        applicationLogLevel: INFO
        systemLogLevel: WARN
    options:
      dependsOn:
        - ${lambdaLogs}
        - ${example}

The execution role’s assumeRolePolicy allows Lambda to assume the role. The logging policy grants permissions to create log groups, streams, and write log events. The RolePolicyAttachment connects the policy to the role. The function depends on both the policy attachment and the log group being ready before it can write logs successfully.

Beyond these examples

These snippets focus on specific Lambda function features: container images and Lambda Layers, VPC networking and EFS mounts, and structured logging and error handling. They’re intentionally minimal rather than full serverless applications.

The examples may reference pre-existing infrastructure such as IAM execution roles, VPC subnets and security groups, ECR repositories, EFS file systems, CloudWatch Log Groups, and SQS queues or SNS topics for dead letter handling. They focus on configuring the function rather than provisioning everything around it.

To keep things focused, common Lambda patterns are omitted, including:

  • Environment variables and KMS encryption (environment, kmsKeyArn)
  • Memory and timeout tuning (memorySize, timeout)
  • Concurrency controls (reservedConcurrentExecutions)
  • Function versioning and aliases (publish, publishTo)
  • Event source triggers (EventBridge, S3, SNS, API Gateway)
  • Code deployment from S3 (s3Bucket, s3Key, s3ObjectVersion)

These omissions are intentional: the goal is to illustrate how each Lambda feature is wired, not provide drop-in serverless modules. See the Lambda Function resource reference for all available configuration options.

Let's create and Configure AWS Lambda Functions

Get started with Pulumi Cloud, then follow our quick setup guide to deploy this infrastructure.

Try Pulumi Cloud for FREE

Frequently Asked Questions

Common Errors & Troubleshooting
Why am I getting KMSAccessDeniedException when invoking my function?
This error occurs when the function’s IAM role was deleted and recreated after the function was created, causing Lambda to lose KMS permissions for decrypting environment variables. Fix it by updating the function’s role to a different role, then updating it back to the recreated role, or by recreating the function.
How long does it take to delete a VPC-enabled Lambda function?
VPC-enabled functions can take up to 45 minutes to delete due to Elastic Network Interface (ENI) cleanup. Provider version 2.31.0 and later automatically handle this increased timeout.
Which Lambda Layer attribute should I use in provider version 2.x?
Use the arn attribute when referencing Lambda Layer Versions in provider 2.x. The arn and layerArn attributes were swapped in version 2.0.0 of the provider.
Code Deployment
How do I deploy my function code?
You have three options: use code with a local file path, use s3Bucket and s3Key for S3-hosted packages, or use imageUri for container images from ECR. These options are mutually exclusive. For larger packages, S3 is recommended.
What's the difference between codeSha256 and sourceCodeHash?
codeSha256 is AWS-computed and captures out-of-band source code changes. sourceCodeHash is a synthetic argument tracked only by the provider that triggers updates when local source code changes, but doesn’t capture out-of-band changes.
What properties can't I change after creating a function?
The name, packageType, and tenancyConfig properties are immutable after creation. Changing these requires recreating the function.
Execution & Performance
What are the timeout limits for Lambda functions?
Lambda functions default to 3 seconds with a maximum of 900 seconds (15 minutes).
What are the memory limits for Lambda functions?
Memory ranges from 128 MB to 32,768 MB (32 GB) in 1 MB increments, with a default of 128 MB.
How do I control Lambda function concurrency?
Use reservedConcurrentExecutions to set reserved concurrency. A value of 0 disables the function from being triggered, and -1 (default) removes concurrency limitations.
How much ephemeral storage can I allocate?
Configure ephemeralStorage to allocate storage in the /tmp directory. The exact limits are specified in the ephemeralStorage configuration block.
Networking & VPC
How do I connect my Lambda function to a VPC?
Configure vpcConfig with subnetIds and securityGroupIds. Optionally set ipv6AllowedForDualStack to true for dual-stack networking.
How do I integrate Lambda with EFS?
Configure fileSystemConfig with the EFS access point ARN and localMountPath (e.g., /mnt/data). Ensure the function depends on EFS mount targets using dependsOn.
How do I replace security groups before destroying a VPC function?
Set replaceSecurityGroupsOnDestroy to true and provide replacementSecurityGroupIds with the new security group IDs.
Logging & Monitoring
What IAM permissions does Lambda need for CloudWatch Logs?
The execution role needs logs:CreateLogGroup, logs:CreateLogStream, and logs:PutLogEvents permissions.
How do I configure JSON logging for Lambda?
Set loggingConfig with logFormat set to JSON, and specify applicationLogLevel and systemLogLevel (e.g., INFO, WARN).
How do I export Lambda logs to S3?
Create a CloudWatch Log Group with logGroupClass set to DELIVERY and a subscription filter with destinationArn pointing to your S3 bucket. Reference the log group name in the function’s loggingConfig.
Advanced Features
How do I attach Lambda layers to my function?
Use the layers property with a list of Layer Version ARNs (maximum of 5). For provider version 2.x, reference LayerVersion.arn.
What should I know about durable functions?
Durable functions (configured with durableConfig) may only be available in limited regions like us-east-2. Deletion can take up to 60 minutes, so configure deletion timeouts accordingly.
What's the difference between the role and aws.lambda.Permission?
The role is the function’s execution role for accessing AWS services and resources. aws.lambda.Permission grants external sources (like EventBridge, SNS, or S3) permission to invoke the function.