Skip to content

Add human-in-the-loop-cdk: CDK version of one-click email approval wo…#417

Open
YogeshNain2015 wants to merge 7 commits into
aws-samples:mainfrom
YogeshNain2015:feature/human-in-the-loop-cdk
Open

Add human-in-the-loop-cdk: CDK version of one-click email approval wo…#417
YogeshNain2015 wants to merge 7 commits into
aws-samples:mainfrom
YogeshNain2015:feature/human-in-the-loop-cdk

Conversation

@YogeshNain2015

Copy link
Copy Markdown

Summary

CDK (Python) implementation of the existing human-in-the-loop pattern with one-click email approval.

  • AWS CDK stack (Python) that creates all resources: Lambda functions, SNS topic, API Gateway, and Step Functions state machine
  • Reuses the same Lambda code and state machine definition from the SAM version (merged in PR Improve human-in-the-loop with one-click email approval #416)
  • All Lambda functions configured with 512 MB memory and 30s timeout
  • Follows the same CDK pattern structure as other -cdk workflows in this repo

Resources created

  • SendApprovalEmailFunction (Python 3.13): URL-encodes task token, sends email via SNS
  • HandleApprovalFunction (Python 3.13): API Gateway callback handler, calls SendTaskSuccess
  • ProcessingLambda (Node.js 24.x): Placeholder for post-approval business logic
  • Amazon SNS Topic: Email subscription for approval notifications
  • Amazon API Gateway: REST API with GET /respond endpoint
  • AWS Step Functions: State machine with waitForTaskToken integration

Related

Issue #, if available:

Description of changes:

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

…rkflow

CDK (Python) implementation of the human-in-the-loop pattern with
one-click email approval. Uses AWS Lambda to URL-encode the task token
and send clickable approve/reject links via Amazon SNS, with Amazon API
Gateway handling the callback to resume the AWS Step Functions workflow.
api_endpoint = f"https://{approval_api.rest_api_id}.execute-api.{self.region}.amazonaws.com/prod/respond"

send_approval_email_fn = _lambda.Function(self, "SendApprovalEmailFunction",
runtime=_lambda.Runtime.PYTHON_3_13,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use the latest available runtime version

notification_topic.grant_publish(send_approval_email_fn)

handle_approval_fn = _lambda.Function(self, "HandleApprovalFunction",
runtime=_lambda.Runtime.PYTHON_3_13,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see above

)

processing_fn = _lambda.Function(self, "ProcessingLambda",
runtime=_lambda.Runtime.NODEJS_24_X,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why mix two programming languages?

@@ -0,0 +1,80 @@
{
"title": "Human in the Loop (CDK)",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"title": "Human in the Loop (CDK)",
"title": "Human in the Loop (AWS CDK)",

"cleanup": {
"headline": "Cleanup",
"text": [
"1. Delete the stack: <code>cdk destroy</code>."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"1. Delete the stack: <code>cdk destroy</code>."
"Delete the stack: <code>cdk destroy</code>."

Comment thread human-in-the-loop-cdk/README.md Outdated
```

----
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
Copyright 2026 Amazon.com, Inc. or its affiliates. All Rights Reserved.

"FunctionName": "${ProcessingLambda}",
"Payload": {
"input.$": "$",
"result": "rejected"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this processed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both "result": "failure" and "result": "rejected" are passed as payload to ProcessingLambda (defined in cdk/cdk_stack.py). The ProcessingLambda is a simple lambda that returns the event as-is i.e currently its not doing anything however it's meant to be replaced by users with their own business logic to handle each outcome differently (e.g., send a rejection notification, trigger a rollback, log the failure, etc.). The result field allows the downstream function to differentiate which path was taken.

"FunctionName": "${ProcessingLambda}",
"Payload": {
"input.$": "$",
"result": "failure"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this processed?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both "result": "failure" and "result": "rejected" are passed as payload to ProcessingLambda (defined in cdk/cdk_stack.py). The ProcessingLambda is a simple lambda that returns the event as-is i.e currently its not doing anything however it's meant to be replaced by users with their own business logic to handle each outcome differently (e.g., send a rejection notification, trigger a rollback, log the failure, etc.). The result field allows the downstream function to differentiate which path was taken.

)
)

state_machine = sfn.CfnStateMachine(self, "HumanInTheLoopStateMachine",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not use sfn.StateMachine?

handler="send_approval_email.lambda_handler",
code=_lambda.Code.from_asset("lambda"),
timeout=Duration.seconds(30),
memory_size=512,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The memory is much higher than in the SAM version for all functions. Any particular reason? Same for the timeout

- Use latest Python runtime (3.14) for all Lambda functions
- Remove Node.js ProcessingLambda, rewrite in Python (single language)
- Use L2 sfn.StateMachine construct instead of CfnStateMachine
- Set Lambda timeout to 10s and updated 128MB memory use
- Fix title to "Human in the Loop (AWS CDK)"
- Remove numbered prefix from cleanup text
- Add copyright year 2026
- Add Comment fields to rejection/failure states for clarity
- In normal scenrio the failure processing lambda is unlikely to be invoked but if someone chages the code handleapprovalfunction specially sendtasktoken in that case it would go to hadlefailure flow, also it can be removed as well. feel free to suggest on the same...
@YogeshNain2015

Copy link
Copy Markdown
Author

Updated below changes:

  • Use latest Python runtime (3.14) for all Lambda functions
  • Remove Node.js ProcessingLambda, rewrite in Python (single language)
  • Use L2 sfn.StateMachine construct instead of CfnStateMachine
  • Set Lambda timeout to 10s and updated 128MB memory use
  • Fix title to "Human in the Loop (AWS CDK)"
  • Remove numbered prefix from cleanup text
  • Add copyright year 2026
  • Add Comment fields to rejection/failure states for clarity
  • In normal scenerio the failure processing lambda is unlikely to be invoked but if someone changes the code handleapprovalfunction specially sendtasktoken in that case it would go to hadlefailure flow, also it can be removed as well. feel free to suggest on the same...

Comment thread human-in-the-loop-cdk/cdk/cdk_stack.py
Comment thread human-in-the-loop-cdk/README.md Outdated
```bash
cdk deploy --parameters ModeratorEmailAddress=your-email@example.com
```
1. Note the outputs from the CDK deployment. These contain the resource names and/or ARNs which are used for testing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's be more precise. It's the link to the state machine in the Step Functions console

Yogesh Nain and others added 4 commits June 9, 2026 18:40
- Rename human_in_the_loop_cdk/ to cdk/ to avoid name duplication
- Clarify deployment output as link to state machine in Step Functions console
Co-authored-by: Ben <9841563+bfreiberg@users.noreply.github.com>
Co-authored-by: Ben <9841563+bfreiberg@users.noreply.github.com>

@bfreiberg bfreiberg left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for your contribution. Your workflow will be merged soon

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants