Invoke AWS Lambda Functions

The aws:lambda/invocation:Invocation resource, part of the Pulumi AWS provider, invokes a Lambda function synchronously during Pulumi operations, optionally re-invoking when inputs change or during lifecycle transitions. This guide focuses on three capabilities: one-time initialization invocations, trigger-based re-invocation, and lifecycle event handling.

Invocations require existing Lambda functions with appropriate IAM permissions. The examples are intentionally small. Combine them with your own Lambda functions and error handling logic.

Invoke a function once during stack creation

Many deployments run initialization logic exactly once when infrastructure is first created, such as seeding databases or validating connectivity.

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

// Lambda function to invoke
const example = new aws.lambda.Function("example", {
    code: new pulumi.asset.FileArchive("function.zip"),
    name: "data_processor",
    role: lambdaRole.arn,
    handler: "index.handler",
    runtime: aws.lambda.Runtime.Python3d12,
});
// Invoke the function once during resource creation
const exampleInvocation = new aws.lambda.Invocation("example", {
    functionName: example.name,
    input: JSON.stringify({
        operation: "initialize",
        config: {
            environment: "production",
            debug: false,
        },
    }),
});
export const initializationResult = std.jsondecodeOutput({
    input: exampleInvocation.result,
}).apply(invoke => invoke.result?.status);
import pulumi
import json
import pulumi_aws as aws
import pulumi_std as std

# Lambda function to invoke
example = aws.lambda_.Function("example",
    code=pulumi.FileArchive("function.zip"),
    name="data_processor",
    role=lambda_role["arn"],
    handler="index.handler",
    runtime=aws.lambda_.Runtime.PYTHON3D12)
# Invoke the function once during resource creation
example_invocation = aws.lambda_.Invocation("example",
    function_name=example.name,
    input=json.dumps({
        "operation": "initialize",
        "config": {
            "environment": "production",
            "debug": False,
        },
    }))
pulumi.export("initializationResult", std.jsondecode_output(input=example_invocation.result).apply(lambda invoke: invoke.result["status"]))
package main

import (
	"encoding/json"

	"github.com/pulumi/pulumi-aws/sdk/v7/go/aws/lambda"
	"github.com/pulumi/pulumi-std/sdk/go/std"
	"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
)
func main() {
pulumi.Run(func(ctx *pulumi.Context) error {
// Lambda function to invoke
example, err := lambda.NewFunction(ctx, "example", &lambda.FunctionArgs{
Code: pulumi.NewFileArchive("function.zip"),
Name: pulumi.String("data_processor"),
Role: pulumi.Any(lambdaRole.Arn),
Handler: pulumi.String("index.handler"),
Runtime: pulumi.String(lambda.RuntimePython3d12),
})
if err != nil {
return err
}
tmpJSON0, err := json.Marshal(map[string]interface{}{
"operation": "initialize",
"config": map[string]interface{}{
"environment": "production",
"debug": false,
},
})
if err != nil {
return err
}
json0 := string(tmpJSON0)
// Invoke the function once during resource creation
exampleInvocation, err := lambda.NewInvocation(ctx, "example", &lambda.InvocationArgs{
FunctionName: example.Name,
Input: pulumi.String(json0),
})
if err != nil {
return err
}
ctx.Export("initializationResult", std.JsondecodeOutput(ctx, std.JsondecodeOutputArgs{
Input: exampleInvocation.Result,
}, nil).ApplyT(func(invoke std.JsondecodeResult) (*interface{}, error) {
return invoke.Result.Status, nil
}).(pulumi.Interface{}PtrOutput))
return nil
})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    // Lambda function to invoke
    var example = new Aws.Lambda.Function("example", new()
    {
        Code = new FileArchive("function.zip"),
        Name = "data_processor",
        Role = lambdaRole.Arn,
        Handler = "index.handler",
        Runtime = Aws.Lambda.Runtime.Python3d12,
    });

    // Invoke the function once during resource creation
    var exampleInvocation = new Aws.Lambda.Invocation("example", new()
    {
        FunctionName = example.Name,
        Input = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["operation"] = "initialize",
            ["config"] = new Dictionary<string, object?>
            {
                ["environment"] = "production",
                ["debug"] = false,
            },
        }),
    });

    return new Dictionary<string, object?>
    {
        ["initializationResult"] = Std.Jsondecode.Invoke(new()
        {
            Input = exampleInvocation.Result,
        }).Apply(invoke => invoke.Result?.Status),
    };
});
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.Invocation;
import com.pulumi.aws.lambda.InvocationArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.JsondecodeArgs;
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) {
        // Lambda function to invoke
        var example = new Function("example", FunctionArgs.builder()
            .code(new FileArchive("function.zip"))
            .name("data_processor")
            .role(lambdaRole.arn())
            .handler("index.handler")
            .runtime("python3.12")
            .build());

        // Invoke the function once during resource creation
        var exampleInvocation = new Invocation("exampleInvocation", InvocationArgs.builder()
            .functionName(example.name())
            .input(serializeJson(
                jsonObject(
                    jsonProperty("operation", "initialize"),
                    jsonProperty("config", jsonObject(
                        jsonProperty("environment", "production"),
                        jsonProperty("debug", false)
                    ))
                )))
            .build());

        ctx.export("initializationResult", StdFunctions.jsondecode(JsondecodeArgs.builder()
            .input(exampleInvocation.result())
            .build()).applyValue(_invoke -> _invoke.result().status()));
    }
}
resources:
  # Lambda function to invoke
  example:
    type: aws:lambda:Function
    properties:
      code:
        fn::FileArchive: function.zip
      name: data_processor
      role: ${lambdaRole.arn}
      handler: index.handler
      runtime: python3.12
  # Invoke the function once during resource creation
  exampleInvocation:
    type: aws:lambda:Invocation
    name: example
    properties:
      functionName: ${example.name}
      input:
        fn::toJSON:
          operation: initialize
          config:
            environment: production
            debug: false
outputs:
  # Use the result in other resources
  initializationResult:
    fn::invoke:
      function: std:jsondecode
      arguments:
        input: ${exampleInvocation.result}
      return: result.status

The functionName property identifies which Lambda to invoke. The input property contains the JSON payload sent to the function. By default, the function runs only during resource creation; subsequent applies don’t re-invoke unless the resource is replaced. The result property captures the function’s return value, which you can parse and export.

Re-invoke when configuration or dependencies change

Some workflows need to re-run Lambda logic whenever upstream configuration changes, such as processing new data batches or responding to environment updates.

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

const example = new aws.lambda.Invocation("example", {
    functionName: exampleAwsLambdaFunction.functionName,
    triggers: {
        function_version: exampleAwsLambdaFunction.version,
        config_hash: std.sha256Output({
            input: JSON.stringify({
                environment: environment,
                timestamp: std.timestamp({}).then(invoke => invoke.result),
            }),
        }).apply(invoke => invoke.result),
    },
    input: JSON.stringify({
        operation: "process_data",
        environment: environment,
        batch_id: batchId.result,
    }),
});
import pulumi
import json
import pulumi_aws as aws
import pulumi_std as std

example = aws.lambda_.Invocation("example",
    function_name=example_aws_lambda_function["functionName"],
    triggers={
        "function_version": example_aws_lambda_function["version"],
        "config_hash": std.sha256_output(input=json.dumps({
            "environment": environment,
            "timestamp": std.timestamp().result,
        })).apply(lambda invoke: invoke.result),
    },
    input=json.dumps({
        "operation": "process_data",
        "environment": environment,
        "batch_id": batch_id["result"],
    }))
package main

import (
	"encoding/json"

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

func main() {
	pulumi.Run(func(ctx *pulumi.Context) error {
		tmpJSON0, err := json.Marshal(map[string]interface{}{
			"environment": environment,
			"timestamp":   std.Timestamp(ctx, &std.TimestampArgs{}, nil).Result,
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		tmpJSON1, err := json.Marshal(map[string]interface{}{
			"operation":   "process_data",
			"environment": environment,
			"batch_id":    batchId.Result,
		})
		if err != nil {
			return err
		}
		json1 := string(tmpJSON1)
		_, err = lambda.NewInvocation(ctx, "example", &lambda.InvocationArgs{
			FunctionName: pulumi.Any(exampleAwsLambdaFunction.FunctionName),
			Triggers: pulumi.StringMap{
				"function_version": pulumi.Any(exampleAwsLambdaFunction.Version),
				"config_hash": pulumi.String(std.Sha256Output(ctx, std.Sha256OutputArgs{
					Input: pulumi.String(json0),
				}, nil).ApplyT(func(invoke std.Sha256Result) (*string, error) {
					return invoke.Result, nil
				}).(pulumi.StringPtrOutput)),
			},
			Input: pulumi.String(json1),
		})
		if err != nil {
			return err
		}
		return nil
	})
}
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Pulumi;
using Aws = Pulumi.Aws;
using Std = Pulumi.Std;

return await Deployment.RunAsync(() => 
{
    var example = new Aws.Lambda.Invocation("example", new()
    {
        FunctionName = exampleAwsLambdaFunction.FunctionName,
        Triggers = 
        {
            { "function_version", exampleAwsLambdaFunction.Version },
            { "config_hash", Std.Sha256.Invoke(new()
            {
                Input = JsonSerializer.Serialize(new Dictionary<string, object?>
                {
                    ["environment"] = environment,
                    ["timestamp"] = Std.Timestamp.Invoke().Apply(invoke => invoke.Result),
                }),
            }).Apply(invoke => invoke.Result) },
        },
        Input = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["operation"] = "process_data",
            ["environment"] = environment,
            ["batch_id"] = batchId.Result,
        }),
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lambda.Invocation;
import com.pulumi.aws.lambda.InvocationArgs;
import com.pulumi.std.StdFunctions;
import com.pulumi.std.inputs.TimestampArgs;
import com.pulumi.std.inputs.Sha256Args;
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 example = new Invocation("example", InvocationArgs.builder()
            .functionName(exampleAwsLambdaFunction.functionName())
            .triggers(Map.ofEntries(
                Map.entry("function_version", exampleAwsLambdaFunction.version()),
                Map.entry("config_hash", StdFunctions.sha256(Sha256Args.builder()
                    .input(serializeJson(
                        jsonObject(
                            jsonProperty("environment", environment),
                            jsonProperty("timestamp", StdFunctions.timestamp(TimestampArgs.builder()
                                .build()).result())
                        )))
                    .build()).applyValue(_invoke -> _invoke.result()))
            ))
            .input(serializeJson(
                jsonObject(
                    jsonProperty("operation", "process_data"),
                    jsonProperty("environment", environment),
                    jsonProperty("batch_id", batchId.result())
                )))
            .build());

    }
}
resources:
  example:
    type: aws:lambda:Invocation
    properties:
      functionName: ${exampleAwsLambdaFunction.functionName}
      triggers:
        function_version: ${exampleAwsLambdaFunction.version}
        config_hash:
          fn::invoke:
            function: std:sha256
            arguments:
              input:
                fn::toJSON:
                  environment: ${environment}
                  timestamp:
                    fn::invoke:
                      function: std:timestamp
                      arguments: {}
                      return: result
            return: result
      input:
        fn::toJSON:
          operation: process_data
          environment: ${environment}
          batch_id: ${batchId.result}

The triggers property accepts a map of arbitrary keys and values. When any trigger value changes, Pulumi re-invokes the function with the updated input. This example uses function version and a config hash as triggers, ensuring the function runs whenever either changes. The input payload can reference dynamic values that vary between invocations.

Handle create, update, and delete lifecycle events

Infrastructure provisioning often requires coordination with external systems that need notification during resource creation, updates, and cleanup.

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

const example = new aws.lambda.Invocation("example", {
    functionName: exampleAwsLambdaFunction.functionName,
    input: JSON.stringify({
        resource_name: "database_setup",
        database_url: exampleAwsDbInstance.endpoint,
        credentials: {
            username: dbUsername,
            password: dbPassword,
        },
    }),
    lifecycleScope: "CRUD",
});
import pulumi
import json
import pulumi_aws as aws

example = aws.lambda_.Invocation("example",
    function_name=example_aws_lambda_function["functionName"],
    input=json.dumps({
        "resource_name": "database_setup",
        "database_url": example_aws_db_instance["endpoint"],
        "credentials": {
            "username": db_username,
            "password": db_password,
        },
    }),
    lifecycle_scope="CRUD")
package main

import (
	"encoding/json"

	"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{}{
			"resource_name": "database_setup",
			"database_url":  exampleAwsDbInstance.Endpoint,
			"credentials": map[string]interface{}{
				"username": dbUsername,
				"password": dbPassword,
			},
		})
		if err != nil {
			return err
		}
		json0 := string(tmpJSON0)
		_, err = lambda.NewInvocation(ctx, "example", &lambda.InvocationArgs{
			FunctionName:   pulumi.Any(exampleAwsLambdaFunction.FunctionName),
			Input:          pulumi.String(json0),
			LifecycleScope: pulumi.String("CRUD"),
		})
		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 example = new Aws.Lambda.Invocation("example", new()
    {
        FunctionName = exampleAwsLambdaFunction.FunctionName,
        Input = JsonSerializer.Serialize(new Dictionary<string, object?>
        {
            ["resource_name"] = "database_setup",
            ["database_url"] = exampleAwsDbInstance.Endpoint,
            ["credentials"] = new Dictionary<string, object?>
            {
                ["username"] = dbUsername,
                ["password"] = dbPassword,
            },
        }),
        LifecycleScope = "CRUD",
    });

});
package generated_program;

import com.pulumi.Context;
import com.pulumi.Pulumi;
import com.pulumi.core.Output;
import com.pulumi.aws.lambda.Invocation;
import com.pulumi.aws.lambda.InvocationArgs;
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 example = new Invocation("example", InvocationArgs.builder()
            .functionName(exampleAwsLambdaFunction.functionName())
            .input(serializeJson(
                jsonObject(
                    jsonProperty("resource_name", "database_setup"),
                    jsonProperty("database_url", exampleAwsDbInstance.endpoint()),
                    jsonProperty("credentials", jsonObject(
                        jsonProperty("username", dbUsername),
                        jsonProperty("password", dbPassword)
                    ))
                )))
            .lifecycleScope("CRUD")
            .build());

    }
}
resources:
  example:
    type: aws:lambda:Invocation
    properties:
      functionName: ${exampleAwsLambdaFunction.functionName}
      input:
        fn::toJSON:
          resource_name: database_setup
          database_url: ${exampleAwsDbInstance.endpoint}
          credentials:
            username: ${dbUsername}
            password: ${dbPassword}
      lifecycleScope: CRUD

Setting lifecycleScope to “CRUD” causes Pulumi to invoke the function during all lifecycle transitions. Pulumi injects a “tf” key into the input JSON with two subkeys: “action” (create, update, or delete) and “prev_input” (the previous invocation’s payload). Your Lambda function can inspect these values to handle each lifecycle event appropriately. During updates, prev_input contains the old configuration; during deletes, it contains the final configuration before removal.

Beyond these examples

These snippets focus on specific invocation features: one-time and trigger-based invocation, and lifecycle event handling. They’re intentionally minimal rather than full orchestration solutions.

The examples assume pre-existing infrastructure such as Lambda functions with appropriate IAM execution roles, and IAM permissions to invoke Lambda functions. They focus on configuring the invocation rather than provisioning the Lambda functions themselves.

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

  • Function versioning and aliases (qualifier)
  • Error handling and retry logic
  • Asynchronous invocation patterns
  • Custom lifecycle key naming (terraformKey)

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

Let's invoke 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

Invocation Behavior & Triggers
Why isn't my Lambda function being invoked on every apply?
By default, the invocation resource only calls your Lambda function during creation or replacement. If your arguments don’t change between applies, the function won’t be invoked again. To trigger re-invocation, use the triggers property with values that change when you want the function to run (like a timestamp or configuration hash).
How do I make my Lambda function run whenever specific values change?
Use the triggers property with a map of keys and values. When any trigger value changes, the function is re-invoked. For example, set function_version or a hash of your configuration as trigger values.
CRUD Lifecycle Management
What's the difference between CREATE_ONLY and CRUD lifecycle scopes?
CREATE_ONLY (default) invokes your Lambda function only during resource creation or replacement. CRUD invokes the function on every lifecycle event: create, update, and delete. With CRUD, your function receives additional lifecycle information in the input payload.
What is the tf key that appears in my Lambda input when using CRUD mode?
When lifecycleScope is set to CRUD, a tf key is automatically injected into your input JSON. It contains action (create/update/delete) and prev_input (the previous invocation payload). If you need to use tf for your own data, override the key name with the terraformKey argument.
How can my Lambda function distinguish between create, update, and delete operations?
Set lifecycleScope to CRUD. Your function will receive a tf.action field with values create, update, or delete. For updates and deletes, tf.prev_input contains the previous invocation’s input payload.
Common Errors & Troubleshooting
Why am I getting KMSAccessDeniedException when invoking my Lambda function?
This error occurs when the IAM role associated with your Lambda function was deleted and recreated after the function was created. Lambda’s KMS grants become invalid when the role is recreated. Fix this by updating the function’s role to a different role, then changing it back to the recreated role, or by recreating the function entirely.
What happens when I import an invocation resource?
After importing, the next Pulumi update will invoke the function again because previous invocation results cannot be retrieved during import.

Using a different cloud?

Explore serverless guides for other cloud providers: