Configure AWS Lambda Permissions

The aws:lambda/permission:Permission resource, part of the Pulumi AWS provider, grants external AWS services and accounts permission to invoke Lambda functions by adding resource-based policy statements. This guide focuses on four capabilities: SNS and EventBridge invocation, API Gateway integration, CloudWatch Logs processing, and cross-account function URL access.

Permissions reference existing Lambda functions and the AWS services that will invoke them. The examples are intentionally small. Combine them with your own Lambda functions, event sources, and IAM configuration.

Allow SNS topics to invoke your function

Event-driven architectures use SNS to fan out messages to multiple subscribers, including Lambda functions.

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

const _default = new aws.sns.Topic("default", {name: "call-lambda-maybe"});
const defaultRole = new aws.iam.Role("default", {
    name: "iam_for_lambda_with_sns",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: "sts:AssumeRole",
            Effect: "Allow",
            Sid: "",
            Principal: {
                Service: "lambda.amazonaws.com",
            },
        }],
    }),
});
const func = new aws.lambda.Function("func", {
    code: new pulumi.asset.FileArchive("lambdatest.zip"),
    name: "lambda_called_from_sns",
    role: defaultRole.arn,
    handler: "exports.handler",
    runtime: aws.lambda.Runtime.Python3d12,
});
const withSns = new aws.lambda.Permission("with_sns", {
    statementId: "AllowExecutionFromSNS",
    action: "lambda:InvokeFunction",
    "function": func.name,
    principal: "sns.amazonaws.com",
    sourceArn: _default.arn,
});
const lambda = new aws.sns.TopicSubscription("lambda", {
    topic: _default.arn,
    protocol: "lambda",
    endpoint: func.arn,
});
import pulumi
import json
import pulumi_aws as aws

default = aws.sns.Topic("default", name="call-lambda-maybe")
default_role = aws.iam.Role("default",
    name="iam_for_lambda_with_sns",
    assume_role_policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Sid": "",
            "Principal": {
                "Service": "lambda.amazonaws.com",
            },
        }],
    }))
func = aws.lambda_.Function("func",
    code=pulumi.FileArchive("lambdatest.zip"),
    name="lambda_called_from_sns",
    role=default_role.arn,
    handler="exports.handler",
    runtime=aws.lambda_.Runtime.PYTHON3D12)
with_sns = aws.lambda_.Permission("with_sns",
    statement_id="AllowExecutionFromSNS",
    action="lambda:InvokeFunction",
    function=func.name,
    principal="sns.amazonaws.com",
    source_arn=default.arn)
lambda_ = aws.sns.TopicSubscription("lambda",
    topic=default.arn,
    protocol="lambda",
    endpoint=func.arn)
package main

import (
	"encoding/json"

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := sns.NewTopic(ctx, "default", &sns.TopicArgs{
			Name: pulumi.String("call-lambda-maybe"),
		})
		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",
					"Sid":    "",
					"Principal": map[string]interface{}{
						"Service": "lambda.amazonaws.com",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		defaultRole, err := iam.NewRole(ctx, "default", &iam.RoleArgs{
			Name:             pulumi.String("iam_for_lambda_with_sns"),
			AssumeRolePolicy: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		_func, err := lambda.NewFunction(ctx, "func", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("lambdatest.zip"),
			Name:    pulumi.String("lambda_called_from_sns"),
			Role:    defaultRole.Arn,
			Handler: pulumi.String("exports.handler"),
			Runtime: pulumi.String(lambda.RuntimePython3d12),
		})
		if err != nil {
			return err
		}
		_, err = lambda.NewPermission(ctx, "with_sns", &lambda.PermissionArgs{
			StatementId: pulumi.String("AllowExecutionFromSNS"),
			Action:      pulumi.String("lambda:InvokeFunction"),
			Function:    _func.Name,
			Principal:   pulumi.String("sns.amazonaws.com"),
			SourceArn:   _default.Arn,
		})
		if err != nil {
			return err
		}
		_, err = sns.NewTopicSubscription(ctx, "lambda", &sns.TopicSubscriptionArgs{
			Topic:    _default.Arn,
			Protocol: pulumi.String("lambda"),
			Endpoint: _func.Arn,
		})
		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 @default = new Aws.Sns.Topic("default", new()
    {
        Name = "call-lambda-maybe",
    });

    var defaultRole = new Aws.Iam.Role("default", new()
    {
        Name = "iam_for_lambda_with_sns",
        AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Action"] = "sts:AssumeRole",
                    ["Effect"] = "Allow",
                    ["Sid"] = "",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "lambda.amazonaws.com",
                    },
                },
            },
        }),
    });

    var func = new Aws.Lambda.Function("func", new()
    {
        Code = new FileArchive("lambdatest.zip"),
        Name = "lambda_called_from_sns",
        Role = defaultRole.Arn,
        Handler = "exports.handler",
        Runtime = Aws.Lambda.Runtime.Python3d12,
    });

    var withSns = new Aws.Lambda.Permission("with_sns", new()
    {
        StatementId = "AllowExecutionFromSNS",
        Action = "lambda:InvokeFunction",
        Function = func.Name,
        Principal = "sns.amazonaws.com",
        SourceArn = @default.Arn,
    });

    var lambda = new Aws.Sns.TopicSubscription("lambda", new()
    {
        Topic = @default.Arn,
        Protocol = "lambda",
        Endpoint = func.Arn,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.sns.Topic;
import com.pulumi.aws.sns.TopicArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.Permission;
import com.pulumi.aws.lambda.PermissionArgs;
import com.pulumi.aws.sns.TopicSubscription;
import com.pulumi.aws.sns.TopicSubscriptionArgs;
import com.pulumi.asset.FileArchive;
import static com.pulumi.codegen.internal.Serialization.*;
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 default_ = new Topic("default", TopicArgs.builder()
            .name("call-lambda-maybe")
            .build());

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

        var func = new Function("func", FunctionArgs.builder()
            .code(new FileArchive("lambdatest.zip"))
            .name("lambda_called_from_sns")
            .role(defaultRole.arn())
            .handler("exports.handler")
            .runtime("python3.12")
            .build());

        var withSns = new Permission("withSns", PermissionArgs.builder()
            .statementId("AllowExecutionFromSNS")
            .action("lambda:InvokeFunction")
            .function(func.name())
            .principal("sns.amazonaws.com")
            .sourceArn(default_.arn())
            .build());

        var lambda = new TopicSubscription("lambda", TopicSubscriptionArgs.builder()
            .topic(default_.arn())
            .protocol("lambda")
            .endpoint(func.arn())
            .build());

    }
}
resources:
  withSns:
    type: aws:lambda:Permission
    name: with_sns
    properties:
      statementId: AllowExecutionFromSNS
      action: lambda:InvokeFunction
      function: ${func.name}
      principal: sns.amazonaws.com
      sourceArn: ${default.arn}
  default:
    type: aws:sns:Topic
    properties:
      name: call-lambda-maybe
  lambda:
    type: aws:sns:TopicSubscription
    properties:
      topic: ${default.arn}
      protocol: lambda
      endpoint: ${func.arn}
  func:
    type: aws:lambda:Function
    properties:
      code:
        fn::FileArchive: lambdatest.zip
      name: lambda_called_from_sns
      role: ${defaultRole.arn}
      handler: exports.handler
      runtime: python3.12
  defaultRole:
    type: aws:iam:Role
    name: default
    properties:
      name: iam_for_lambda_with_sns
      assumeRolePolicy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Action: sts:AssumeRole
              Effect: Allow
              Sid: ""
              Principal:
                Service: lambda.amazonaws.com

The principal property specifies the AWS service (sns.amazonaws.com), while sourceArn identifies the specific SNS topic. The action property grants lambda:InvokeFunction permission. After creating the permission, the TopicSubscription resource connects the topic to the function.

Grant EventBridge rules permission to invoke functions

Scheduled tasks and event-driven workflows use EventBridge rules to trigger Lambda functions on a schedule or in response to AWS service events.

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

const iamForLambda = new aws.iam.Role("iam_for_lambda", {
    name: "iam_for_lambda",
    assumeRolePolicy: JSON.stringify({
        Version: "2012-10-17",
        Statement: [{
            Action: "sts:AssumeRole",
            Effect: "Allow",
            Sid: "",
            Principal: {
                Service: "lambda.amazonaws.com",
            },
        }],
    }),
});
const testLambda = new aws.lambda.Function("test_lambda", {
    code: new pulumi.asset.FileArchive("lambdatest.zip"),
    name: "lambda_function_name",
    role: iamForLambda.arn,
    handler: "exports.handler",
    runtime: aws.lambda.Runtime.NodeJS20dX,
});
const testAlias = new aws.lambda.Alias("test_alias", {
    name: "testalias",
    description: "a sample description",
    functionName: testLambda.name,
    functionVersion: "$LATEST",
});
const allowCloudwatch = new aws.lambda.Permission("allow_cloudwatch", {
    statementId: "AllowExecutionFromCloudWatch",
    action: "lambda:InvokeFunction",
    "function": testLambda.name,
    principal: "events.amazonaws.com",
    sourceArn: "arn:aws:events:eu-west-1:111122223333:rule/RunDaily",
    qualifier: testAlias.name,
});
import pulumi
import json
import pulumi_aws as aws

iam_for_lambda = aws.iam.Role("iam_for_lambda",
    name="iam_for_lambda",
    assume_role_policy=json.dumps({
        "Version": "2012-10-17",
        "Statement": [{
            "Action": "sts:AssumeRole",
            "Effect": "Allow",
            "Sid": "",
            "Principal": {
                "Service": "lambda.amazonaws.com",
            },
        }],
    }))
test_lambda = aws.lambda_.Function("test_lambda",
    code=pulumi.FileArchive("lambdatest.zip"),
    name="lambda_function_name",
    role=iam_for_lambda.arn,
    handler="exports.handler",
    runtime=aws.lambda_.Runtime.NODE_JS20D_X)
test_alias = aws.lambda_.Alias("test_alias",
    name="testalias",
    description="a sample description",
    function_name=test_lambda.name,
    function_version="$LATEST")
allow_cloudwatch = aws.lambda_.Permission("allow_cloudwatch",
    statement_id="AllowExecutionFromCloudWatch",
    action="lambda:InvokeFunction",
    function=test_lambda.name,
    principal="events.amazonaws.com",
    source_arn="arn:aws:events:eu-west-1:111122223333:rule/RunDaily",
    qualifier=test_alias.name)
package main

import (
	"encoding/json"

	"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"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"Version": "2012-10-17",
			"Statement": []map[string]interface{}{
				map[string]interface{}{
					"Action": "sts:AssumeRole",
					"Effect": "Allow",
					"Sid":    "",
					"Principal": map[string]interface{}{
						"Service": "lambda.amazonaws.com",
					},
				},
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		iamForLambda, err := iam.NewRole(ctx, "iam_for_lambda", &iam.RoleArgs{
			Name:             pulumi.String("iam_for_lambda"),
			AssumeRolePolicy: pulumi.String(json0),
		})
		if err != nil {
			return err
		}
		testLambda, err := lambda.NewFunction(ctx, "test_lambda", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("lambdatest.zip"),
			Name:    pulumi.String("lambda_function_name"),
			Role:    iamForLambda.Arn,
			Handler: pulumi.String("exports.handler"),
			Runtime: pulumi.String(lambda.RuntimeNodeJS20dX),
		})
		if err != nil {
			return err
		}
		testAlias, err := lambda.NewAlias(ctx, "test_alias", &lambda.AliasArgs{
			Name:            pulumi.String("testalias"),
			Description:     pulumi.String("a sample description"),
			FunctionName:    testLambda.Name,
			FunctionVersion: pulumi.String("$LATEST"),
		})
		if err != nil {
			return err
		}
		_, err = lambda.NewPermission(ctx, "allow_cloudwatch", &lambda.PermissionArgs{
			StatementId: pulumi.String("AllowExecutionFromCloudWatch"),
			Action:      pulumi.String("lambda:InvokeFunction"),
			Function:    testLambda.Name,
			Principal:   pulumi.String("events.amazonaws.com"),
			SourceArn:   pulumi.String("arn:aws:events:eu-west-1:111122223333:rule/RunDaily"),
			Qualifier:   testAlias.Name,
		})
		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 iamForLambda = new Aws.Iam.Role("iam_for_lambda", new()
    {
        Name = "iam_for_lambda",
        AssumeRolePolicy = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["Version"] = "2012-10-17",
            ["Statement"] = new[]
            {
                new Dictionary<string, object?>
                {
                    ["Action"] = "sts:AssumeRole",
                    ["Effect"] = "Allow",
                    ["Sid"] = "",
                    ["Principal"] = new Dictionary<string, object?>
                    {
                        ["Service"] = "lambda.amazonaws.com",
                    },
                },
            },
        }),
    });

    var testLambda = new Aws.Lambda.Function("test_lambda", new()
    {
        Code = new FileArchive("lambdatest.zip"),
        Name = "lambda_function_name",
        Role = iamForLambda.Arn,
        Handler = "exports.handler",
        Runtime = Aws.Lambda.Runtime.NodeJS20dX,
    });

    var testAlias = new Aws.Lambda.Alias("test_alias", new()
    {
        Name = "testalias",
        Description = "a sample description",
        FunctionName = testLambda.Name,
        FunctionVersion = "$LATEST",
    });

    var allowCloudwatch = new Aws.Lambda.Permission("allow_cloudwatch", new()
    {
        StatementId = "AllowExecutionFromCloudWatch",
        Action = "lambda:InvokeFunction",
        Function = testLambda.Name,
        Principal = "events.amazonaws.com",
        SourceArn = "arn:aws:events:eu-west-1:111122223333:rule/RunDaily",
        Qualifier = testAlias.Name,
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.Alias;
import com.pulumi.aws.lambda.AliasArgs;
import com.pulumi.aws.lambda.Permission;
import com.pulumi.aws.lambda.PermissionArgs;
import com.pulumi.asset.FileArchive;
import static com.pulumi.codegen.internal.Serialization.*;
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 iamForLambda = new Role("iamForLambda", RoleArgs.builder()
            .name("iam_for_lambda")
            .assumeRolePolicy(serializeJson(
                jsonObject(
                    jsonProperty("Version", "2012-10-17"),
                    jsonProperty("Statement", jsonArray(jsonObject(
                        jsonProperty("Action", "sts:AssumeRole"),
                        jsonProperty("Effect", "Allow"),
                        jsonProperty("Sid", ""),
                        jsonProperty("Principal", jsonObject(
                            jsonProperty("Service", "lambda.amazonaws.com")
                        ))
                    )))
                )))
            .build());

        var testLambda = new Function("testLambda", FunctionArgs.builder()
            .code(new FileArchive("lambdatest.zip"))
            .name("lambda_function_name")
            .role(iamForLambda.arn())
            .handler("exports.handler")
            .runtime("nodejs20.x")
            .build());

        var testAlias = new Alias("testAlias", AliasArgs.builder()
            .name("testalias")
            .description("a sample description")
            .functionName(testLambda.name())
            .functionVersion("$LATEST")
            .build());

        var allowCloudwatch = new Permission("allowCloudwatch", PermissionArgs.builder()
            .statementId("AllowExecutionFromCloudWatch")
            .action("lambda:InvokeFunction")
            .function(testLambda.name())
            .principal("events.amazonaws.com")
            .sourceArn("arn:aws:events:eu-west-1:111122223333:rule/RunDaily")
            .qualifier(testAlias.name())
            .build());

    }
}
resources:
  allowCloudwatch:
    type: aws:lambda:Permission
    name: allow_cloudwatch
    properties:
      statementId: AllowExecutionFromCloudWatch
      action: lambda:InvokeFunction
      function: ${testLambda.name}
      principal: events.amazonaws.com
      sourceArn: arn:aws:events:eu-west-1:111122223333:rule/RunDaily
      qualifier: ${testAlias.name}
  testAlias:
    type: aws:lambda:Alias
    name: test_alias
    properties:
      name: testalias
      description: a sample description
      functionName: ${testLambda.name}
      functionVersion: $LATEST
  testLambda:
    type: aws:lambda:Function
    name: test_lambda
    properties:
      code:
        fn::FileArchive: lambdatest.zip
      name: lambda_function_name
      role: ${iamForLambda.arn}
      handler: exports.handler
      runtime: nodejs20.x
  iamForLambda:
    type: aws:iam:Role
    name: iam_for_lambda
    properties:
      name: iam_for_lambda
      assumeRolePolicy:
        fn::toJSON:
          Version: 2012-10-17
          Statement:
            - Action: sts:AssumeRole
              Effect: Allow
              Sid: ""
              Principal:
                Service: lambda.amazonaws.com

The qualifier property targets a specific function version or alias rather than $LATEST. The sourceArn references the EventBridge rule that will invoke the function. This extends the SNS pattern by adding version control through aliases.

Allow API Gateway to invoke backend functions

REST APIs built with API Gateway route HTTP requests to Lambda functions that implement business logic.

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

const myDemoAPI = new aws.apigateway.RestApi("MyDemoAPI", {
    name: "MyDemoAPI",
    description: "This is my API for demonstration purposes",
});
const lambdaPermission = new aws.lambda.Permission("lambda_permission", {
    statementId: "AllowMyDemoAPIInvoke",
    action: "lambda:InvokeFunction",
    "function": "MyDemoFunction",
    principal: "apigateway.amazonaws.com",
    sourceArn: pulumi.interpolate`${myDemoAPI.executionArn}/*`,
});
import pulumi
import pulumi_aws as aws

my_demo_api = aws.apigateway.RestApi("MyDemoAPI",
    name="MyDemoAPI",
    description="This is my API for demonstration purposes")
lambda_permission = aws.lambda_.Permission("lambda_permission",
    statement_id="AllowMyDemoAPIInvoke",
    action="lambda:InvokeFunction",
    function="MyDemoFunction",
    principal="apigateway.amazonaws.com",
    source_arn=my_demo_api.execution_arn.apply(lambda execution_arn: f"{execution_arn}/*"))
package main

import (
	"fmt"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/apigateway"
	"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 {
		myDemoAPI, err := apigateway.NewRestApi(ctx, "MyDemoAPI", &apigateway.RestApiArgs{
			Name:        pulumi.String("MyDemoAPI"),
			Description: pulumi.String("This is my API for demonstration purposes"),
		})
		if err != nil {
			return err
		}
		_, err = lambda.NewPermission(ctx, "lambda_permission", &lambda.PermissionArgs{
			StatementId: pulumi.String("AllowMyDemoAPIInvoke"),
			Action:      pulumi.String("lambda:InvokeFunction"),
			Function:    pulumi.Any("MyDemoFunction"),
			Principal:   pulumi.String("apigateway.amazonaws.com"),
			SourceArn: myDemoAPI.ExecutionArn.ApplyT(func(executionArn string) (string, error) {
				return fmt.Sprintf("%v/*", executionArn), nil
			}).(pulumi.StringOutput),
		})
		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 myDemoAPI = new Aws.ApiGateway.RestApi("MyDemoAPI", new()
    {
        Name = "MyDemoAPI",
        Description = "This is my API for demonstration purposes",
    });

    var lambdaPermission = new Aws.Lambda.Permission("lambda_permission", new()
    {
        StatementId = "AllowMyDemoAPIInvoke",
        Action = "lambda:InvokeFunction",
        Function = "MyDemoFunction",
        Principal = "apigateway.amazonaws.com",
        SourceArn = myDemoAPI.ExecutionArn.Apply(executionArn => $"{executionArn}/*"),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.apigateway.RestApi;
import com.pulumi.aws.apigateway.RestApiArgs;
import com.pulumi.aws.lambda.Permission;
import com.pulumi.aws.lambda.PermissionArgs;
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 myDemoAPI = new RestApi("myDemoAPI", RestApiArgs.builder()
            .name("MyDemoAPI")
            .description("This is my API for demonstration purposes")
            .build());

        var lambdaPermission = new Permission("lambdaPermission", PermissionArgs.builder()
            .statementId("AllowMyDemoAPIInvoke")
            .action("lambda:InvokeFunction")
            .function("MyDemoFunction")
            .principal("apigateway.amazonaws.com")
            .sourceArn(myDemoAPI.executionArn().applyValue(_executionArn -> String.format("%s/*", _executionArn)))
            .build());

    }
}
resources:
  myDemoAPI:
    type: aws:apigateway:RestApi
    name: MyDemoAPI
    properties:
      name: MyDemoAPI
      description: This is my API for demonstration purposes
  lambdaPermission:
    type: aws:lambda:Permission
    name: lambda_permission
    properties:
      statementId: AllowMyDemoAPIInvoke
      action: lambda:InvokeFunction
      function: MyDemoFunction
      principal: apigateway.amazonaws.com
      sourceArn: ${myDemoAPI.executionArn}/*

The sourceArn uses the API Gateway’s executionArn with a wildcard to grant permission for all methods and resources. The principal is apigateway.amazonaws.com. API Gateway requires this permission before it can invoke your function.

Process CloudWatch Logs with Lambda subscriptions

Log processing pipelines stream CloudWatch log events to Lambda functions for filtering, transformation, or forwarding.

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

const _default = new aws.cloudwatch.LogGroup("default", {name: "/default"});
const assumeRole = aws.iam.getPolicyDocument({
    statements: [{
        effect: "Allow",
        principals: [{
            type: "Service",
            identifiers: ["lambda.amazonaws.com"],
        }],
        actions: ["sts:AssumeRole"],
    }],
});
const defaultRole = new aws.iam.Role("default", {
    name: "iam_for_lambda_called_from_cloudwatch_logs",
    assumeRolePolicy: assumeRole.then(assumeRole => assumeRole.json),
});
const loggingFunction = new aws.lambda.Function("logging", {
    code: new pulumi.asset.FileArchive("lamba_logging.zip"),
    name: "lambda_called_from_cloudwatch_logs",
    handler: "exports.handler",
    role: defaultRole.arn,
    runtime: aws.lambda.Runtime.Python3d12,
});
const logging = new aws.lambda.Permission("logging", {
    action: "lambda:InvokeFunction",
    "function": loggingFunction.name,
    principal: "logs.eu-west-1.amazonaws.com",
    sourceArn: pulumi.interpolate`${_default.arn}:*`,
});
const loggingLogSubscriptionFilter = new aws.cloudwatch.LogSubscriptionFilter("logging", {
    destinationArn: loggingFunction.arn,
    filterPattern: "",
    logGroup: _default.name,
    name: "logging_default",
}, {
    dependsOn: [logging],
});
import pulumi
import pulumi_aws as aws

default = aws.cloudwatch.LogGroup("default", name="/default")
assume_role = aws.iam.get_policy_document(statements=[{
    "effect": "Allow",
    "principals": [{
        "type": "Service",
        "identifiers": ["lambda.amazonaws.com"],
    }],
    "actions": ["sts:AssumeRole"],
}])
default_role = aws.iam.Role("default",
    name="iam_for_lambda_called_from_cloudwatch_logs",
    assume_role_policy=assume_role.json)
logging_function = aws.lambda_.Function("logging",
    code=pulumi.FileArchive("lamba_logging.zip"),
    name="lambda_called_from_cloudwatch_logs",
    handler="exports.handler",
    role=default_role.arn,
    runtime=aws.lambda_.Runtime.PYTHON3D12)
logging = aws.lambda_.Permission("logging",
    action="lambda:InvokeFunction",
    function=logging_function.name,
    principal="logs.eu-west-1.amazonaws.com",
    source_arn=default.arn.apply(lambda arn: f"{arn}:*"))
logging_log_subscription_filter = aws.cloudwatch.LogSubscriptionFilter("logging",
    destination_arn=logging_function.arn,
    filter_pattern="",
    log_group=default.name,
    name="logging_default",
    opts = pulumi.ResourceOptions(depends_on=[logging]))
package main

import (
	"fmt"

	"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"
)

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		_default, err := cloudwatch.NewLogGroup(ctx, "default", &cloudwatch.LogGroupArgs{
			Name: pulumi.String("/default"),
		})
		if err != nil {
			return err
		}
		assumeRole, err := iam.GetPolicyDocument(ctx, &iam.GetPolicyDocumentArgs{
			Statements: []iam.GetPolicyDocumentStatement{
				{
					Effect: pulumi.StringRef("Allow"),
					Principals: []iam.GetPolicyDocumentStatementPrincipal{
						{
							Type: "Service",
							Identifiers: []string{
								"lambda.amazonaws.com",
							},
						},
					},
					Actions: []string{
						"sts:AssumeRole",
					},
				},
			},
		}, nil)
		if err != nil {
			return err
		}
		defaultRole, err := iam.NewRole(ctx, "default", &iam.RoleArgs{
			Name:             pulumi.String("iam_for_lambda_called_from_cloudwatch_logs"),
			AssumeRolePolicy: pulumi.String(assumeRole.Json),
		})
		if err != nil {
			return err
		}
		loggingFunction, err := lambda.NewFunction(ctx, "logging", &lambda.FunctionArgs{
			Code:    pulumi.NewFileArchive("lamba_logging.zip"),
			Name:    pulumi.String("lambda_called_from_cloudwatch_logs"),
			Handler: pulumi.String("exports.handler"),
			Role:    defaultRole.Arn,
			Runtime: pulumi.String(lambda.RuntimePython3d12),
		})
		if err != nil {
			return err
		}
		logging, err := lambda.NewPermission(ctx, "logging", &lambda.PermissionArgs{
			Action:    pulumi.String("lambda:InvokeFunction"),
			Function:  loggingFunction.Name,
			Principal: pulumi.String("logs.eu-west-1.amazonaws.com"),
			SourceArn: _default.Arn.ApplyT(func(arn string) (string, error) {
				return fmt.Sprintf("%v:*", arn), nil
			}).(pulumi.StringOutput),
		})
		if err != nil {
			return err
		}
		_, err = cloudwatch.NewLogSubscriptionFilter(ctx, "logging", &cloudwatch.LogSubscriptionFilterArgs{
			DestinationArn: loggingFunction.Arn,
			FilterPattern:  pulumi.String(""),
			LogGroup:       _default.Name,
			Name:           pulumi.String("logging_default"),
		}, pulumi.DependsOn([]pulumi.Resource{
			logging,
		}))
		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 @default = new Aws.CloudWatch.LogGroup("default", new()
    {
        Name = "/default",
    });

    var assumeRole = Aws.Iam.GetPolicyDocument.Invoke(new()
    {
        Statements = new[]
        {
            new Aws.Iam.Inputs.GetPolicyDocumentStatementInputArgs
            {
                Effect = "Allow",
                Principals = new[]
                {
                    new Aws.Iam.Inputs.GetPolicyDocumentStatementPrincipalInputArgs
                    {
                        Type = "Service",
                        Identifiers = new[]
                        {
                            "lambda.amazonaws.com",
                        },
                    },
                },
                Actions = new[]
                {
                    "sts:AssumeRole",
                },
            },
        },
    });

    var defaultRole = new Aws.Iam.Role("default", new()
    {
        Name = "iam_for_lambda_called_from_cloudwatch_logs",
        AssumeRolePolicy = assumeRole.Apply(getPolicyDocumentResult => getPolicyDocumentResult.Json),
    });

    var loggingFunction = new Aws.Lambda.Function("logging", new()
    {
        Code = new FileArchive("lamba_logging.zip"),
        Name = "lambda_called_from_cloudwatch_logs",
        Handler = "exports.handler",
        Role = defaultRole.Arn,
        Runtime = Aws.Lambda.Runtime.Python3d12,
    });

    var logging = new Aws.Lambda.Permission("logging", new()
    {
        Action = "lambda:InvokeFunction",
        Function = loggingFunction.Name,
        Principal = "logs.eu-west-1.amazonaws.com",
        SourceArn = @default.Arn.Apply(arn => $"{arn}:*"),
    });

    var loggingLogSubscriptionFilter = new Aws.CloudWatch.LogSubscriptionFilter("logging", new()
    {
        DestinationArn = loggingFunction.Arn,
        FilterPattern = "",
        LogGroup = @default.Name,
        Name = "logging_default",
    }, new CustomResourceOptions
    {
        DependsOn =
        {
            logging,
        },
    });

});
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.IamFunctions;
import com.pulumi.aws.iam.inputs.GetPolicyDocumentArgs;
import com.pulumi.aws.iam.Role;
import com.pulumi.aws.iam.RoleArgs;
import com.pulumi.aws.lambda.Function;
import com.pulumi.aws.lambda.FunctionArgs;
import com.pulumi.aws.lambda.Permission;
import com.pulumi.aws.lambda.PermissionArgs;
import com.pulumi.aws.cloudwatch.LogSubscriptionFilter;
import com.pulumi.aws.cloudwatch.LogSubscriptionFilterArgs;
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 default_ = new LogGroup("default", LogGroupArgs.builder()
            .name("/default")
            .build());

        final var assumeRole = IamFunctions.getPolicyDocument(GetPolicyDocumentArgs.builder()
            .statements(GetPolicyDocumentStatementArgs.builder()
                .effect("Allow")
                .principals(GetPolicyDocumentStatementPrincipalArgs.builder()
                    .type("Service")
                    .identifiers("lambda.amazonaws.com")
                    .build())
                .actions("sts:AssumeRole")
                .build())
            .build());

        var defaultRole = new Role("defaultRole", RoleArgs.builder()
            .name("iam_for_lambda_called_from_cloudwatch_logs")
            .assumeRolePolicy(assumeRole.json())
            .build());

        var loggingFunction = new Function("loggingFunction", FunctionArgs.builder()
            .code(new FileArchive("lamba_logging.zip"))
            .name("lambda_called_from_cloudwatch_logs")
            .handler("exports.handler")
            .role(defaultRole.arn())
            .runtime("python3.12")
            .build());

        var logging = new Permission("logging", PermissionArgs.builder()
            .action("lambda:InvokeFunction")
            .function(loggingFunction.name())
            .principal("logs.eu-west-1.amazonaws.com")
            .sourceArn(default_.arn().applyValue(_arn -> String.format("%s:*", _arn)))
            .build());

        var loggingLogSubscriptionFilter = new LogSubscriptionFilter("loggingLogSubscriptionFilter", LogSubscriptionFilterArgs.builder()
            .destinationArn(loggingFunction.arn())
            .filterPattern("")
            .logGroup(default_.name())
            .name("logging_default")
            .build(), CustomResourceOptions.builder()
                .dependsOn(logging)
                .build());

    }
}
resources:
  logging:
    type: aws:lambda:Permission
    properties:
      action: lambda:InvokeFunction
      function: ${loggingFunction.name}
      principal: logs.eu-west-1.amazonaws.com
      sourceArn: ${default.arn}:*
  default:
    type: aws:cloudwatch:LogGroup
    properties:
      name: /default
  loggingLogSubscriptionFilter:
    type: aws:cloudwatch:LogSubscriptionFilter
    name: logging
    properties:
      destinationArn: ${loggingFunction.arn}
      filterPattern: ""
      logGroup: ${default.name}
      name: logging_default
    options:
      dependsOn:
        - ${logging}
  loggingFunction:
    type: aws:lambda:Function
    name: logging
    properties:
      code:
        fn::FileArchive: lamba_logging.zip
      name: lambda_called_from_cloudwatch_logs
      handler: exports.handler
      role: ${defaultRole.arn}
      runtime: python3.12
  defaultRole:
    type: aws:iam:Role
    name: default
    properties:
      name: iam_for_lambda_called_from_cloudwatch_logs
      assumeRolePolicy: ${assumeRole.json}
variables:
  assumeRole:
    fn::invoke:
      function: aws:iam:getPolicyDocument
      arguments:
        statements:
          - effect: Allow
            principals:
              - type: Service
                identifiers:
                  - lambda.amazonaws.com
            actions:
              - sts:AssumeRole

The principal uses a region-specific format (logs.eu-west-1.amazonaws.com). The LogSubscriptionFilter resource depends on the permission to ensure the grant exists before CloudWatch Logs attempts invocation. The sourceArn includes a wildcard suffix to match all log streams in the group.

Grant cross-account access to function URLs

Function URLs provide HTTP endpoints for Lambda functions. Cross-account access with IAM authentication requires explicit permission.

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

const url = new aws.lambda.FunctionUrl("url", {
    functionName: example.functionName,
    authorizationType: "AWS_IAM",
});
const urlPermission = new aws.lambda.Permission("url", {
    action: "lambda:InvokeFunctionUrl",
    "function": example.functionName,
    principal: "arn:aws:iam::444455556666:role/example",
    sourceAccount: "444455556666",
    functionUrlAuthType: "AWS_IAM",
});
import pulumi
import pulumi_aws as aws

url = aws.lambda_.FunctionUrl("url",
    function_name=example["functionName"],
    authorization_type="AWS_IAM")
url_permission = aws.lambda_.Permission("url",
    action="lambda:InvokeFunctionUrl",
    function=example["functionName"],
    principal="arn:aws:iam::444455556666:role/example",
    source_account="444455556666",
    function_url_auth_type="AWS_IAM")
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.NewFunctionUrl(ctx, "url", &lambda.FunctionUrlArgs{
			FunctionName:      pulumi.Any(example.FunctionName),
			AuthorizationType: pulumi.String("AWS_IAM"),
		})
		if err != nil {
			return err
		}
		_, err = lambda.NewPermission(ctx, "url", &lambda.PermissionArgs{
			Action:              pulumi.String("lambda:InvokeFunctionUrl"),
			Function:            pulumi.Any(example.FunctionName),
			Principal:           pulumi.String("arn:aws:iam::444455556666:role/example"),
			SourceAccount:       pulumi.String("444455556666"),
			FunctionUrlAuthType: pulumi.String("AWS_IAM"),
		})
		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 url = new Aws.Lambda.FunctionUrl("url", new()
    {
        FunctionName = example.FunctionName,
        AuthorizationType = "AWS_IAM",
    });

    var urlPermission = new Aws.Lambda.Permission("url", new()
    {
        Action = "lambda:InvokeFunctionUrl",
        Function = example.FunctionName,
        Principal = "arn:aws:iam::444455556666:role/example",
        SourceAccount = "444455556666",
        FunctionUrlAuthType = "AWS_IAM",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lambda.FunctionUrl;
import com.pulumi.aws.lambda.FunctionUrlArgs;
import com.pulumi.aws.lambda.Permission;
import com.pulumi.aws.lambda.PermissionArgs;
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 url = new FunctionUrl("url", FunctionUrlArgs.builder()
            .functionName(example.functionName())
            .authorizationType("AWS_IAM")
            .build());

        var urlPermission = new Permission("urlPermission", PermissionArgs.builder()
            .action("lambda:InvokeFunctionUrl")
            .function(example.functionName())
            .principal("arn:aws:iam::444455556666:role/example")
            .sourceAccount("444455556666")
            .functionUrlAuthType("AWS_IAM")
            .build());

    }
}
resources:
  url:
    type: aws:lambda:FunctionUrl
    properties:
      functionName: ${example.functionName}
      authorizationType: AWS_IAM
  urlPermission:
    type: aws:lambda:Permission
    name: url
    properties:
      action: lambda:InvokeFunctionUrl
      function: ${example.functionName}
      principal: arn:aws:iam::444455556666:role/example
      sourceAccount: '444455556666'
      functionUrlAuthType: AWS_IAM

The action changes to lambda:InvokeFunctionUrl for function URL invocations. The principal specifies an IAM role ARN from another account, and sourceAccount restricts access to that account. The functionUrlAuthType must match the function URL’s authentication setting.

Beyond these examples

These snippets focus on specific permission features: service-to-Lambda invocation (SNS, EventBridge, API Gateway, CloudWatch Logs), function versioning with qualifiers, and cross-account access with IAM authentication. They’re intentionally minimal rather than full event-driven architectures.

The examples reference pre-existing infrastructure such as Lambda functions and aliases, SNS topics, EventBridge rules, API Gateway REST APIs, CloudWatch Log Groups, and IAM roles for cross-account access. They focus on granting permissions rather than provisioning the event sources or functions.

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

  • S3 bucket event notifications (sourceAccount usage)
  • Organization-wide permissions (principalOrgId)
  • Alexa Skills integration (eventSourceToken)
  • Function URL public access (NONE auth type)

These omissions are intentional: the goal is to illustrate how each permission pattern is wired, not provide drop-in event-driven modules. See the Lambda Permission resource reference for all available configuration options.

Let's configure AWS Lambda Permissions

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

Try Pulumi Cloud for FREE

Frequently Asked Questions

Permissions & Access Control
What's the difference between Lambda Permission and the IAM role?
Lambda Permission grants external sources (like S3, SNS, or EventBridge) permission to invoke your function. The IAM role grants your function permission to access other AWS services.
What principal should I use for different AWS services?
Use service-specific principals: events.amazonaws.com for EventBridge, sns.amazonaws.com for SNS, s3.amazonaws.com for S3, apigateway.amazonaws.com for API Gateway, and logs.<region>.amazonaws.com for CloudWatch Logs.
How do I grant permission to all accounts in my AWS Organization?
Use principalOrgId with your AWS Organizations ID instead of specifying individual account principals.
Configuration & Required Fields
When should I specify sourceArn?
Specify sourceArn to restrict which specific resource can invoke your function, such as a particular S3 bucket, SNS topic, or EventBridge rule.
What's the difference between sourceArn and sourceAccount?
sourceArn identifies a specific resource (like an S3 bucket ARN), while sourceAccount identifies an AWS account ID for cross-account access, S3, or SES.
When do I need to specify qualifier?
Use qualifier to grant permission to a specific Lambda function version or alias instead of the entire function.
When do I need functionUrlAuthType?
Only when using lambda:InvokeFunctionUrl action. Set it to AWS_IAM for IAM-authenticated access or NONE for public access.
Immutability & Updates
Can I change the principal or action after creating the permission?
No, properties like action, function, principal, sourceArn, and qualifier are immutable. Changing them requires replacing the permission resource.
What's the difference between statementId and statementIdPrefix?
statementId sets an explicit identifier, while statementIdPrefix generates one automatically. You cannot specify both; they conflict with each other.
Service Integration
Why do I need dependsOn when integrating with CloudWatch Logs?
The LogSubscriptionFilter must wait for the Lambda permission to be created first, otherwise the subscription will fail. Add dependsOn: [logging] to the subscription resource.
How do I grant cross-account access to my Lambda function?
Set principal to the IAM role ARN from the other account and specify sourceAccount with the account ID.

Using a different cloud?

Explore security guides for other cloud providers: