Skip to content

New serverless pattern - oneclick-manualapproval-stepfunctionworkflow #2795

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 80 additions & 0 deletions oneclick-stepfunction-human-approval/example-pattern.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"title": "AWS Step Functions Manual Approval Workflow with SNS",
"description": "Create a serverless manual approval process within Step Functions workflows using SNS notifications and API Gateway.",
"language": "Python",
"level": "200",
"framework": "AWS SAM",
"introBox": {
"headline": "How it works",
"text": [
"This pattern creates a Step Functions workflow that pauses for human approval, using SNS for email notifications and API Gateway for handling responses.",
"The workflow sends approval requests via SNS email notifications containing approve/reject links.",
"Recipients can approve or reject the workflow by clicking links that trigger an API Gateway endpoint.",
"The pattern demonstrates how to incorporate human decision points within automated Step Functions workflows.",
"Please consider AWS Step Functions state transitions quotas and API Gateway limits when implementing this pattern."
]
},
"gitHub": {
"template": {
"repoURL": "<your-repo-url>",
"templateURL": "serverless-patterns/stepfunctions-manual-approval",
"projectFolder": "stepfunctions-manual-approval",
"templateFile": "template.yaml"
}
},
"resources": {
"bullets": [
{
"text": "AWS Step Functions Task Tokens",
"link": "https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#connect-wait-token"
},
{
"text": "Step Functions Service Integration Patterns",
"link": "https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html"
},
{
"text": "AWS Step Functions Developer Guide",
"link": "https://docs.aws.amazon.com/step-functions/latest/dg/welcome.html"
},
{
"text": "AWS SNS Developer Guide",
"link": "https://docs.aws.amazon.com/sns/latest/dg/welcome.html"
},
{
"text": "AWS Step Functions Quotas",
"link": "https://docs.aws.amazon.com/step-functions/latest/dg/limits.html"
}
]
},
"deploy": {
"text": [
"sam build --use-container",
"sam deploy --guided"
]
},
"testing": {
"text": [
"1. Start a workflow execution using the AWS CLI:",
"export STATE_MACHINE_ARN=$(aws cloudformation describe-stacks --stack-name approval-workflow --query \"Stacks[0].Outputs[?OutputKey=='StateMachineArn'].OutputValue\" --output text)",
"aws stepfunctions start-execution --state-machine-arn $STATE_MACHINE_ARN --input \"{\\\"key\\\": \\\"value\\\"}\"",
"2. Check your email for the approval request",
"3. Click the approve or reject link in the email",
"4. Verify the workflow execution status in Step Functions console"
]
},
"cleanup": {
"text": [
"1. Delete the stack: <code>sam delete</code>",
"2. Confirm stack deletion when prompted",
"3. Check SNS topic and unsubscribe from any remaining email subscriptions"
]
},
"authors": [
{
"name": "Yogesh Nain",
"image": "link-to-your-photo.jpg",
"bio": "Yogesh is Cloud Support Engineer and SME of Lambda, API Gateway at Amazon Web Services.",
"linkedin": "https://www.linkedin.com/in/yogesh-nain-a54133170/"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
133 changes: 133 additions & 0 deletions oneclick-stepfunction-human-approval/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
# One Click Manual Approval Workflow in Stepfunction

This pattern creates a serverless approval workflow using AWS Step Functions, Lambda, SNS, and API Gateway. The workflow sends approval requests via email and handles approvals through a REST API endpoint.

## Requirements

* An AWS account with appropriate IAM permissions
* AWS CLI installed and configured
* Git installed
* AWS Serverless Application Model (AWS SAM) CLI installed
* Python 3.13 or later
* A verified email address to receive approval requests

## Deployment Instructions

1. Clone the repository:
```bash
git clone <repository-url>
cd serverless-approval-workflow
```

2. Deploy the application:
```bash
sam build --use-container
sam deploy --guided
```

During the prompts:
- Enter a stack name (e.g., approval-workflow)
- Select your desired AWS Region
- Enter the email address for receiving approval requests
- Allow SAM CLI to create IAM roles with required permissions
- After the initial deployment, subsequent deployments can use sam deploy with the saved configuration (samconfig.toml).

## How it works
1. The workflow starts when a Step Functions state machine execution is initiated
2. A Lambda function sends an approval request via SNS to the specified email address
3. The email contains approve/reject links that point to an API Gateway endpoint
4. When the recipient clicks either link, the API Gateway triggers a Lambda function
5. The Lambda function sends the approval/rejection response back to Step Functions
6. The workflow completes based on the approval decision
![](img/digram.png)

## Usage Example and Consideration

This pattern is particularly valuable when you need to incorporate human decision-making into your AWS Step Functions workflows. Here are key use cases and benefits in the context of Step Functions:
- Change management processes
- Release approvals
- Resource provisioning requests
- Access grant workflows
- Content moderation workflows

### Use Cases:
1. **Multi-stage Deployment Pipelines**
- Pause an automated deployment for manual verification and approval before proceeding to production.

2. **Data Processing Workflows**
- Allow human verification of processed data before triggering downstream systems.

3. **Resource Provisioning**
- Require manual approval for provisioning high-cost or sensitive resources within an automated workflow.

4. **Compliance and Governance**
- Enforce mandatory human sign-off for actions that require oversight due to regulatory or policy requirements eg: when updating IAM policy through Step Function.

5. **Error Handling and Remediation**
- Incorporate human intervention in complex error scenarios where automated resolution is not possible.


This pattern demonstrates how to enhance your Step Functions workflows with manual approvals, striking a balance between automation and human oversight in critical processes.

Please review Step Functions, Lambda, and API Gateway service quotas before implementation.

## Testing
1. Start a workflow execution:
```bash
# Get the State Machine ARN
export STATE_MACHINE_ARN=$(aws cloudformation describe-stacks \
--stack-name approval-workflow \
--query "Stacks[0].Outputs[?OutputKey=='StateMachineArn'].OutputValue" \
--output text)
```
```bash
# Start execution
aws stepfunctions start-execution \
--state-machine-arn $STATE_MACHINE_ARN \
--input "{\"key\": \"value\"}"
```


2. Check your email for the approval request

3. Click the approve or reject link in the email

4. Check the execution status:
```bash
aws stepfunctions describe-execution --execution-arn <execution-arn>
```


## Expected Output

Clicking the API Gateway Url from the approval mail will have below output based on approval status:

API response when approved:
```bash
{
"message": "Workflow approved successfully"
}
```

API response when rejected
```bash
{
"message": "Workflow rejected"
}
```

## Cleanup
Delete the stack using SAM:

```bash
sam delete
```
Note:

- Confirm when prompted to delete the stack and its resources
- You may need to manually delete any SNS subscriptions
- Use sam delete --no-prompts to skip confirmation steps

Important: This application uses various AWS services that may incur costs beyond the AWS Free Tier. Please review the AWS Pricing page for details. You are responsible for any AWS costs incurred. No warranty is implied in this example.

For additional information about AWS Step Functions service integrations, see the [AWS Step Functions Developer Guide](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html).
42 changes: 42 additions & 0 deletions oneclick-stepfunction-human-approval/src/handle_approval.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import boto3
import json
from urllib.parse import unquote

def lambda_handler(event, context):
query_params = event.get('queryStringParameters', {})
# URL decode the task token
task_token = unquote(query_params.get('taskToken', ''))
decision = query_params.get('decision')

sfn = boto3.client('stepfunctions')

try:
if decision == 'approve':
sfn.send_task_success(
taskToken=task_token,
output=json.dumps({'approved': True})
)
message = "Workflow approved successfully"
else:
sfn.send_task_failure(
taskToken=task_token,
error='Rejected',
cause='User rejected the workflow'
)
message = "Workflow rejected"

return {
'statusCode': 200,
'body': json.dumps({'message': message}),
'headers': {
'Content-Type': 'application/json'
}
}
except Exception as e:
return {
'statusCode': 500,
'body': json.dumps({'error': str(e)}),
'headers': {
'Content-Type': 'application/json'
}
}
1 change: 1 addition & 0 deletions oneclick-stepfunction-human-approval/src/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
boto3
42 changes: 42 additions & 0 deletions oneclick-stepfunction-human-approval/src/send_approval_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import boto3
import json
import os
from urllib.parse import quote

def lambda_handler(event, context):
task_token = quote(event['taskToken']) # URL encode the task token
execution_id = event['execution']

api_endpoint = os.environ['API_ENDPOINT']
approve_url = f"{api_endpoint}?taskToken={task_token}&decision=approve"
reject_url = f"{api_endpoint}?taskToken={task_token}&decision=reject"

# Create message in SNS message format
message_data = {
'default': 'Workflow Approval Required',
'email': f"""
Workflow Approval Required - {execution_id}

A new approval is required for workflow execution {execution_id}

To approve: {approve_url}

To reject: {reject_url}
"""
}

sns = boto3.client('sns')
try:
# Publish message with MessageStructure as 'json'
response = sns.publish(
TopicArn=os.environ['SNS_TOPIC_ARN'],
Message=json.dumps(message_data),
MessageStructure='json'
)
return {
'statusCode': 200,
'body': 'Approval notification sent successfully'
}
except Exception as e:
print(str(e))
raise
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"Comment": "Manual approval workflow with pass/fail states",
"StartAt": "Manual Approval",
"States": {
"Manual Approval": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke.waitForTaskToken",
"Parameters": {
"FunctionName": "${SendApprovalEmailFunctionArn}",
"Payload": {
"execution.$": "$$.Execution.Id",
"taskToken.$": "$$.Task.Token",
"input.$": "$"
}
},
"Next": "Process Approval",
"TimeoutSeconds": 3600
},
"Process Approval": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.approved",
"BooleanEquals": true,
"Next": "Success Pass State"
}
],
"Default": "Fail"
},
"Success Pass State": {
"Type": "Pass",
"Result": {
"status": "success",
"message": "Workflow approved and completed successfully"
},
"End": true
},
"Fail": {
"Type": "Fail",
"Cause": "Workflow rejected by user"
}
}
}
Loading