Skip to content

Commit 144e018

Browse files
committed
Made changes to main.tf and readme
1 parent 3588703 commit 144e018

3 files changed

Lines changed: 131 additions & 55 deletions

File tree

lambda-durable-function-chaining-terraform/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Function chaining with AWS Lambda durable functions in Terraform
22

3-
This Terraform pattern demonstrates function chaining using AWS Lambda durable functions. A durable orchestrator invokes three Lambda functions in sequence (add, transform and finalize). The output of each step is passed as the input to the next. The framework automatically checkpoints after every step, so if the orchestrator fails mid-workflow, it replays from the beginning and skips already-completed work. This ensures exactly-once execution without re-processing.
3+
This Terraform pattern demonstrates function chaining using AWS Lambda durable functions. A durable orchestrator invokes three Lambda functions in sequence (add, transform and finalize). The output of each step is passed as the input to the next. The durable functions framework automatically checkpoints after every step, so if the orchestrator fails mid-workflow, it replays from the beginning and skips already-completed work. This ensures exactly-once execution without re-processing.
44

55
Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/lambda-durable-function-chaining-terraform
66

@@ -11,7 +11,7 @@ Important: this application uses various AWS services and there are costs associ
1111
* [Create an AWS account](https://portal.aws.amazon.com/gp/aws/developer/registration/index.html) if you do not already have one and log in. The IAM user that you use must have sufficient permissions to make necessary AWS service calls and manage AWS resources.
1212
* [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured
1313
* [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
14-
* [Terraform](https://learn.hashicorp.cxom/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed
14+
* [Terraform](https://learn.hashicorp.com/tutorials/terraform/install-cli?in=terraform/aws-get-started) installed
1515

1616
## Deployment Instructions
1717

@@ -46,7 +46,7 @@ Important: this application uses various AWS services and there are costs associ
4646
4747
This pattern uses a durable orchestrator to chain three Lambda functions in sequence. Each function receives the output of the previous step as its input, forming a pipeline: **Add → Transform → Finalize**.
4848
49-
At each `context.invoke()` call, the durable framework creates a checkpoint. If the orchestrator fails at any point, it automatically replays from the beginning but skips already-completed steps using stored results, ensuring no work is repeated. Workflows resume from the last successful checkpoint, recovering automatically without manual intervention.
49+
At each `context.invoke()` call, the durable function creates a checkpoint. If the orchestrator fails at any point, it automatically replays from the beginning but skips already-completed steps using stored results, ensuring no work is repeated. Workflows resume from the last successful checkpoint, recovering automatically without manual intervention.
5050
5151
Consider the following input,
5252

lambda-durable-function-chaining-terraform/main.tf

Lines changed: 128 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ terraform {
1010
source = "hashicorp/aws"
1111
version = "~> 6.32.1"
1212
}
13+
archive = {
14+
source = "hashicorp/archive"
15+
version = "~> 2.7"
16+
}
1317
}
1418
}
1519

@@ -52,11 +56,11 @@ variable "log_retention_days" {
5256
}
5357

5458
############################################################
55-
# IAM Role for All Lambda Functions
59+
# IAM Role – Worker Lambda Functions (non-durable)
5660
############################################################
5761

58-
resource "aws_iam_role" "lambda_role" {
59-
name = "${var.prefix}-durable-orchestrator-role"
62+
resource "aws_iam_role" "worker_role" {
63+
name = "${var.prefix}-worker-role"
6064

6165
force_detach_policies = true
6266

@@ -70,13 +74,32 @@ resource "aws_iam_role" "lambda_role" {
7074
})
7175
}
7276

73-
resource "aws_iam_role_policy_attachment" "basic_execution" {
74-
role = aws_iam_role.lambda_role.name
77+
resource "aws_iam_role_policy_attachment" "worker_basic_execution" {
78+
role = aws_iam_role.worker_role.name
7579
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
7680
}
7781

82+
############################################################
83+
# IAM Role – Durable Orchestrator Lambda Function
84+
############################################################
85+
86+
resource "aws_iam_role" "orchestrator_role" {
87+
name = "${var.prefix}-durable-orchestrator-role"
88+
89+
force_detach_policies = true
90+
91+
assume_role_policy = jsonencode({
92+
Version = "2012-10-17"
93+
Statement = [{
94+
Effect = "Allow"
95+
Action = "sts:AssumeRole"
96+
Principal = { Service = "lambda.amazonaws.com" }
97+
}]
98+
})
99+
}
100+
78101
resource "aws_iam_role_policy_attachment" "durable_execution" {
79-
role = aws_iam_role.lambda_role.name
102+
role = aws_iam_role.orchestrator_role.name
80103
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicDurableExecutionRolePolicy"
81104
}
82105

@@ -108,32 +131,6 @@ resource "aws_cloudwatch_log_group" "orchestrator" {
108131
skip_destroy = false
109132
}
110133

111-
############################################################
112-
# Scoped CloudWatch Logs Write Policy
113-
############################################################
114-
115-
resource "aws_iam_role_policy" "lambda_logging" {
116-
name = "${var.prefix}-lambda-cloudwatch-logging"
117-
role = aws_iam_role.lambda_role.id
118-
119-
policy = jsonencode({
120-
Version = "2012-10-17"
121-
Statement = [{
122-
Effect = "Allow"
123-
Action = [
124-
"logs:CreateLogStream",
125-
"logs:PutLogEvents"
126-
]
127-
Resource = [
128-
"${aws_cloudwatch_log_group.step1.arn}:*",
129-
"${aws_cloudwatch_log_group.step2.arn}:*",
130-
"${aws_cloudwatch_log_group.step3.arn}:*",
131-
"${aws_cloudwatch_log_group.orchestrator.arn}:*"
132-
]
133-
}]
134-
})
135-
}
136-
137134
############################################################
138135
# Worker Lambda Functions
139136
############################################################
@@ -145,16 +142,15 @@ resource "aws_lambda_function" "step1" {
145142
runtime = "python3.14"
146143
memory_size = 512
147144
timeout = 30
148-
role = aws_iam_role.lambda_role.arn
145+
role = aws_iam_role.worker_role.arn
149146

150147
logging_config {
151148
log_group = aws_cloudwatch_log_group.step1.name
152149
log_format = "Text"
153150
}
154151

155152
depends_on = [
156-
aws_cloudwatch_log_group.step1,
157-
aws_iam_role_policy.lambda_logging
153+
aws_cloudwatch_log_group.step1
158154
]
159155
}
160156

@@ -165,16 +161,15 @@ resource "aws_lambda_function" "step2" {
165161
runtime = "python3.14"
166162
memory_size = 512
167163
timeout = 30
168-
role = aws_iam_role.lambda_role.arn
164+
role = aws_iam_role.worker_role.arn
169165

170166
logging_config {
171167
log_group = aws_cloudwatch_log_group.step2.name
172168
log_format = "Text"
173169
}
174170

175171
depends_on = [
176-
aws_cloudwatch_log_group.step2,
177-
aws_iam_role_policy.lambda_logging
172+
aws_cloudwatch_log_group.step2
178173
]
179174
}
180175

@@ -185,16 +180,15 @@ resource "aws_lambda_function" "step3" {
185180
runtime = "python3.14"
186181
memory_size = 512
187182
timeout = 30
188-
role = aws_iam_role.lambda_role.arn
183+
role = aws_iam_role.worker_role.arn
189184

190185
logging_config {
191186
log_group = aws_cloudwatch_log_group.step3.name
192187
log_format = "Text"
193188
}
194189

195190
depends_on = [
196-
aws_cloudwatch_log_group.step3,
197-
aws_iam_role_policy.lambda_logging
191+
aws_cloudwatch_log_group.step3
198192
]
199193
}
200194

@@ -204,7 +198,7 @@ resource "aws_lambda_function" "step3" {
204198

205199
resource "aws_iam_role_policy" "invoke_workers" {
206200
name = "${var.prefix}-invoke-worker-functions"
207-
role = aws_iam_role.lambda_role.id
201+
role = aws_iam_role.orchestrator_role.id
208202

209203
policy = jsonencode({
210204
Version = "2012-10-17"
@@ -223,19 +217,102 @@ resource "aws_iam_role_policy" "invoke_workers" {
223217
})
224218
}
225219

220+
############################################################
221+
# Inline Orchestrator Source Code
222+
#
223+
# The durable execution SDK (@durable_execution decorator and
224+
# DurableContext) is built into the python3.14 runtime when
225+
# durable_config is set — no extra layer is needed.
226+
############################################################
227+
228+
data "archive_file" "orchestrator" {
229+
type = "zip"
230+
output_path = "${path.module}/orchestrator.zip"
231+
232+
source {
233+
filename = "orchestrator.py"
234+
content = <<-PYTHON
235+
import json
236+
import os
237+
import logging
238+
import boto3
239+
from aws_durable_execution_sdk_python import DurableContext, durable_execution
240+
241+
logger = logging.getLogger()
242+
logger.setLevel(logging.INFO)
243+
244+
lambda_client = boto3.client('lambda')
245+
246+
247+
def call_lambda(function_arn, payload):
248+
"""Invoke a regular Lambda function and return parsed response."""
249+
response = lambda_client.invoke(
250+
FunctionName=function_arn,
251+
InvocationType='RequestResponse',
252+
Payload=json.dumps(payload)
253+
)
254+
255+
if 'FunctionError' in response:
256+
error_payload = json.loads(response['Payload'].read())
257+
raise RuntimeError(f"Function {function_arn} failed: {error_payload}")
258+
259+
return json.loads(response['Payload'].read())
260+
261+
262+
@durable_execution
263+
def lambda_handler(event, context: DurableContext):
264+
step1_arn = os.environ['STEP1_FUNCTION_ARN']
265+
step2_arn = os.environ['STEP2_FUNCTION_ARN']
266+
step3_arn = os.environ['STEP3_FUNCTION_ARN']
267+
268+
logger.info("Orchestrator invoked with event: %s", json.dumps(event, default=str))
269+
270+
# Step 1 — lambda receives the arg context.step() passes; ignored here
271+
result1 = context.step(
272+
lambda _: call_lambda(step1_arn, event),
273+
name='step1-add'
274+
)
275+
logger.info("STEP1 result: %s", json.dumps(result1, default=str))
276+
277+
# Step 2
278+
result2 = context.step(
279+
lambda _: call_lambda(step2_arn, result1),
280+
name='step2-transform'
281+
)
282+
logger.info("STEP2 result: %s", json.dumps(result2, default=str))
283+
284+
# Step 3
285+
result3 = context.step(
286+
lambda _: call_lambda(step3_arn, result2),
287+
name='step3-finalize'
288+
)
289+
logger.info("STEP3 result: %s", json.dumps(result3, default=str))
290+
291+
response = {
292+
'workflow': 'completed',
293+
'input': event,
294+
'output': result3
295+
}
296+
logger.info("Final response: %s", json.dumps(response, default=str))
297+
return response
298+
PYTHON
299+
}
300+
}
301+
226302
############################################################
227303
# Durable Orchestrator Function
228304
############################################################
229305

230306
resource "aws_lambda_function" "orchestrator" {
231-
function_name = "${var.prefix}-durable-orchestrator"
232-
filename = "orchestrator.zip"
233-
handler = "orchestrator.lambda_handler"
234-
runtime = "python3.14"
235-
memory_size = 512
236-
timeout = 90
237-
role = aws_iam_role.lambda_role.arn
238-
publish = true
307+
function_name = "${var.prefix}-durable-orchestrator"
308+
filename = data.archive_file.orchestrator.output_path
309+
source_code_hash = data.archive_file.orchestrator.output_base64sha256
310+
handler = "orchestrator.lambda_handler"
311+
runtime = "python3.14"
312+
memory_size = 512
313+
timeout = 90
314+
role = aws_iam_role.orchestrator_role.arn
315+
publish = true
239316

240317
durable_config {
241318
execution_timeout = 90
@@ -266,8 +343,7 @@ resource "aws_lambda_function" "orchestrator" {
266343
}
267344

268345
depends_on = [
269-
aws_cloudwatch_log_group.orchestrator,
270-
aws_iam_role_policy.lambda_logging
346+
aws_cloudwatch_log_group.orchestrator
271347
]
272348
}
273349

-842 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)