diff --git a/stepfunctions-sns-apigw-human-review/APIGateway/ReviewRESTApi.yaml b/stepfunctions-sns-apigw-human-review/APIGateway/ReviewRESTApi.yaml new file mode 100644 index 000000000..23aa11410 --- /dev/null +++ b/stepfunctions-sns-apigw-human-review/APIGateway/ReviewRESTApi.yaml @@ -0,0 +1,36 @@ +openapi: "3.0.1" +info: + title: "ram-sfn-human-approval" + version: "2024-09-27T16:44:01Z" +paths: + /: + x-amazon-apigateway-any-method: + responses: + "200": + description: "200 response" + content: + application/json: + schema: + $ref: "#/components/schemas/Empty" + x-amazon-apigateway-integration: + type: "aws" + credentials: + Fn::GetAtt: [ReviewRESTApiRole, Arn] + httpMethod: "POST" + uri: "arn:aws:apigateway:${AWS::Region}:states:action/SendTaskSuccess" + responses: + default: + statusCode: "200" + responseTemplates: + application/json: "{\n \"Expense report status\" : \"$util.escapeJavaScript($input.params('Type'))\"\ + \n}" + requestTemplates: + application/json: "{\n \"output\": \"{\\\"Result\\\": \\\"$util.escapeJavaScript($input.params('Type'))\\\ + \"}\",\n \"taskToken\": \"$util.base64Decode($util.escapeJavaScript($input.params('Token')))\"\ + \n}" + passthroughBehavior: "when_no_templates" +components: + schemas: + Empty: + title: "Empty Schema" + type: "object" \ No newline at end of file diff --git a/stepfunctions-sns-apigw-human-review/README.md b/stepfunctions-sns-apigw-human-review/README.md new file mode 100644 index 000000000..24b9b942a --- /dev/null +++ b/stepfunctions-sns-apigw-human-review/README.md @@ -0,0 +1,72 @@ +# Filter and Transform Amazon SQS messages with Amazon EventBridge Pipes + +The application will create a Amazon Step Functions State Machine, an Amazon SNS topic, and an Amazon API Gateway REST API. This pattern allows you to integrate a human review or approval process into your workflows. The State Machine task sends a message to an SNS topic which sends a notification to a human reviewer by email. The workflow then waits until the approver completes their review. When the reviewer makes a selection, it will trigger an API that sends the `SendTaskSuccess` [API](https://docs.aws.amazon.com/step-functions/latest/apireference/API_SendTaskSuccess.html) or or `SendTaskFailure` [API](https://docs.aws.amazon.com/step-functions/latest/apireference/API_SendTaskFailure.html) call with the chosen result. +This pattern is implemented with AWS Serverless Application Model (AWS SAM). + +Learn more about this pattern at Serverless Land Patterns: https://serverlessland.com/patterns/stepfunctions-sns-apigw-human-review + +Important: this application uses various AWS services and there are costs associated with these services after the Free Tier usage - please see the [AWS Pricing page](https://aws.amazon.com/pricing/) for details. You are responsible for any AWS costs incurred. No warranty is implied in this example. + +## Requirements + +- [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. +- [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) installed and configured +- [Git Installed](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) +- [AWS SAM](https://docs.aws.amazon.com/cdk/latest/guide/cli.html) installed and configured + +## Deployment Instructions + +1. Create a new directory, navigate to that directory in a terminal and clone the GitHub repository: + ``` + git clone https://github.com/aws-samples/serverless-patterns + ``` +2. Change directory to the pattern directory: + ``` + cd serverless-patterns/stepfunctions-sns-apigw-human-review/ + ``` +3. From the command line, use AWS SAM to deploy the AWS resources for the pattern as specified in the template.yaml file: + ``` + sam deploy --guided --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM + ``` +4. During the prompts: + * Enter a stack name + * Enter `us-east-1` or any other AWS Region. + * Enter an email address that should receive the notifications from the workflow. + * Allow SAM CLI to create IAM roles with the required permissions. Please keep all other options to default. + +Once you have run `sam deploy --guided` mode once and saved arguments to a configuration file (samconfig.toml), you can use `sam deploy` in future to use these defaults. + +## How it works + +![image](./Resources/statemachine.png) + +1. During the workflow execution, a message is sent to an [Amazon Simple Notification Service (SNS)](https://aws.amazon.com/sns/) topic which sends out a notification via Email. The notification contains links to `Approve` or `Reject` the request. The link contains an API Gateway endpoint with [task token](https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#connect-wait-token), which is automatically generated by AWS Step Functions. +2. After approving or denying, the reviewer calls the `SendTaskSuccess` API and passes the task token as well as the review result. +3. The Step Function assesses the outcome and moves the workflow to the next task. + +## Testing + +1. After deployment you receive an email titled `AWS Notification - Subscription Confirmation`. To confirm your subscription, click on the link provided in the email. This will allow SNS to send you emails. +2. Navigate to the AWS Step Functions console and select the step function workflow. +3. Select `Start Execution` and use the JSON payload listed below as input. + ``` + { + "expense": 900 + } + ``` +4. Select `Start Execution` and wait until you receive the email from SNS. +5. Select the review response as `Approve`. +6. Observe the task in the Step Functions console. Since the response confirms the approval, the task moved to the `Request Approved` step. +7. If you trigger a new execution and select the review response as `Decline` in step 6, the workflow transitions to `Request Rejected` step. + +## Delete stack + +``` +sam delete +``` + +--- + +Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. + +SPDX-License-Identifier: MIT-0 diff --git a/stepfunctions-sns-apigw-human-review/Resources/statemachine.png b/stepfunctions-sns-apigw-human-review/Resources/statemachine.png new file mode 100644 index 000000000..fae555d54 Binary files /dev/null and b/stepfunctions-sns-apigw-human-review/Resources/statemachine.png differ diff --git a/stepfunctions-sns-apigw-human-review/StepFunction/step-function-definition.json b/stepfunctions-sns-apigw-human-review/StepFunction/step-function-definition.json new file mode 100644 index 000000000..5f1706f1d --- /dev/null +++ b/stepfunctions-sns-apigw-human-review/StepFunction/step-function-definition.json @@ -0,0 +1,47 @@ +{ + "Comment": "A description of my state machine", + "StartAt": "Total Expenses >= $500", + "States": { + "Total Expenses >= $500": { + "Type": "Choice", + "Choices": [ + { + "Variable": "$.expense", + "NumericLessThan": 500, + "Next": "Auto approved" + } + ], + "Default": "Get human approval" + }, + "Auto approved": { + "Type": "Succeed" + }, + "Get human approval": { + "Type": "Task", + "Resource": "arn:aws:states:::sns:publish.waitForTaskToken", + "Parameters": { + "TopicArn": "${TopicArn}", + "Message.$": "States.Format('Please review \n\nApprove: \"${ApiEndpoint}?Type=Approved&Token={}\" \n\nDecline: \"${ApiEndpoint}?Type=Rejected&Token={}\"',States.Base64Encode($$.Task.Token), States.Base64Encode($$.Task.Token))", + "Subject": "Expense Approval" + }, + "Next": "Approval/Decline" + }, + "Approval/Decline": { + "Type": "Choice", + "Choices": [ + { + "Variable": "$.Result", + "StringEquals": "Approved", + "Next": "Request Approved" + } + ], + "Default": "Request Rejected" + }, + "Request Approved": { + "Type": "Succeed" + }, + "Request Rejected": { + "Type": "Fail" + } + } +} \ No newline at end of file diff --git a/stepfunctions-sns-apigw-human-review/example-pattern.json b/stepfunctions-sns-apigw-human-review/example-pattern.json new file mode 100644 index 000000000..06fe937dc --- /dev/null +++ b/stepfunctions-sns-apigw-human-review/example-pattern.json @@ -0,0 +1,75 @@ +{ + "title": "Human review process using Amazon Step Functions.", + "description": "Integrate a human review process into your workflow.", + "language": "", + "level": "200", + "framework": "SAM", + "diagram":"/resources/statemachine.png", + "introBox": { + "headline": "How it works", + "text": [ + "This application will create a State Machine, an SNS topic, and an API Gateway REST API.", + "The State Machine sends a message to an SNS topic which sends a notification to a human reviewer by email. The workflow then waits until the approver completes their review. When the reviewer makes a selection, it will trigger an API that sends the SendTaskSuccess API call with the chosen result.", + "Waiting for completion of the review is done using the .waitForTaskToken service integration. The payload of the SNS message contains a task token, which is automatically generated by AWS Step Functions.", + "The task will pause until it receives that task token back with a SendTaskSuccess or SendTaskFailure API call." + + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/stepfunctions-sns-apigw-human-review", + "templateURL": "serverless-patterns/stepfunctions-sns-apigw-human-review", + "projectFolder": "stepfunctions-sns-apigw-human-review", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Discover service integration patterns in Step Functions", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html" + }, + { + "text": "Wait for a Callback with Task Token", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#connect-wait-token" + }, + { + "text": "Calling a Step Functions API using API Gateway", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-api-gateway.html" + }, + { + "text": "Intrinsic functions in Amazon States Language for Step Functions workflows", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/intrinsic-functions.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy --guided --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Sridhar Chevendra", + "image": "https://media.licdn.com/dms/image/v2/C4E03AQFpArv6Yxt3pQ/profile-displayphoto-shrink_400_400/profile-displayphoto-shrink_400_400/0/1517698219415?e=1733356800&v=beta&t=ehp9uqF0PKEz2aNMjv-VGD7koJqfm_wcm7VsFwJaaPY", + "bio":"Sridhar uses customer-centric approach to build secure and scalable cloud architectures.", + "linkedin": "https://www.linkedin.com/in/sridhar-chevendra-92a7916/" + }, + { + "name": "Anup Rajpara", + "image": "https://drive.google.com/file/d/1MqpPNLCqbU4kvvtTspNXZBqD99aVIJI9/view?usp=sharing", + "bio":"Anup is passionate about serverless & event-driven architectures.", + "linkedin": "https://www.linkedin.com/in/anup-rajpara-developer/" + } + ] +} diff --git a/stepfunctions-sns-apigw-human-review/stepfunctions-sns-apigw-human-review.json b/stepfunctions-sns-apigw-human-review/stepfunctions-sns-apigw-human-review.json new file mode 100644 index 000000000..90652323a --- /dev/null +++ b/stepfunctions-sns-apigw-human-review/stepfunctions-sns-apigw-human-review.json @@ -0,0 +1,104 @@ +{ + "title": "Human review process using Amazon Step Functions.", + "description": "Integrate a human review process into your workflow.", + "language": "", + "level": "200", + "framework": "SAM", + "diagram": "/resources/statemachine.png", + "introBox": { + "headline": "How it works", + "text": [ + "The application will create a Amazon Step Functions State Machine, an Amazon SNS topic, and an Amazon API Gateway REST API.", + "The State Machine sends a message to an SNS topic which sends a notification to a human reviewer by email. The workflow then waits until the approver completes their review. When the reviewer makes a selection, it will trigger an API that sends the SendTaskSuccess API call with the chosen result.", + "Waiting for completion of the review is done using the .waitForTaskToken service integration. The payload of the SNS message contains a task token, which is automatically generated by AWS Step Functions.", + "The task will pause until it receives that task token back with a SendTaskSuccess or SendTaskFailure API call." + ] + }, + "gitHub": { + "template": { + "repoURL": "https://github.com/aws-samples/serverless-patterns/tree/main/stepfunctions-sns-apigw-human-review", + "templateURL": "serverless-patterns/stepfunctions-sns-apigw-human-review", + "projectFolder": "stepfunctions-sns-apigw-human-review", + "templateFile": "template.yaml" + } + }, + "resources": { + "bullets": [ + { + "text": "Discover service integration patterns in Step Functions", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html" + }, + { + "text": "Wait for a Callback with Task Token", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/connect-to-resource.html#connect-wait-token" + }, + { + "text": "Calling a Step Functions API using API Gateway", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/tutorial-api-gateway.html" + }, + { + "text": "Intrinsic functions in Amazon States Language for Step Functions workflows", + "link": "https://docs.aws.amazon.com/step-functions/latest/dg/intrinsic-functions.html" + } + ] + }, + "deploy": { + "text": [ + "sam deploy --guided --capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM" + ] + }, + "testing": { + "text": [ + "See the GitHub repo for detailed testing instructions." + ] + }, + "cleanup": { + "text": [ + "Delete the stack: sam delete." + ] + }, + "authors": [ + { + "name": "Sridhar Chevendra", + "image": "https://media.licdn.com/dms/image/v2/C4E03AQFpArv6Yxt3pQ/profile-displayphoto-shrink_400_400/profile-displayphoto-shrink_400_400/0/1517698219415?e=1733356800&v=beta&t=ehp9uqF0PKEz2aNMjv-VGD7koJqfm_wcm7VsFwJaaPY", + "bio": "Sridhar uses customer-centric approach to build secure and scalable cloud architectures.", + "linkedin": "sridhar-chevendra-92a7916/" + }, + { + "name": "Anup Rajpara", + "image": "https://drive.google.com/file/d/1MqpPNLCqbU4kvvtTspNXZBqD99aVIJI9/view?usp=sharing", + "bio": "Anup is passionate about serverless & event-driven architectures.", + "linkedin": "anup-rajpara-developer/" + } + ], + "patternArch": { + "icon1": { + "x": 20, + "y": 50, + "service": "apigw", + "label": "API Gateway REST API" + }, + "icon2": { + "x": 50, + "y": 50, + "service": "sfn", + "label": "Amazon Step Function" + }, + "icon3": { + "x": 80, + "y": 50, + "service": "sns", + "label": "Amazon SNS" + }, + "line1": { + "from": "icon1", + "to": "icon2", + "label": "" + }, + "line2": { + "from": "icon2", + "to": "icon3", + "label": "" + } + } +} diff --git a/stepfunctions-sns-apigw-human-review/template.yaml b/stepfunctions-sns-apigw-human-review/template.yaml new file mode 100644 index 000000000..d9ddb7f6d --- /dev/null +++ b/stepfunctions-sns-apigw-human-review/template.yaml @@ -0,0 +1,91 @@ +AWSTemplateFormatVersion: '2010-09-09' +Transform: AWS::Serverless-2016-10-31 +Description: Sample SAM template to deploy a simplified workflow that waits for human approval in Step Functions. + +Parameters: + ApproverEmailAddress: + Description: Enter the email address that will receive review notifications. + Type: String + +Resources: + SNSNotificationTopic: + Type: AWS::SNS::Topic + Properties: + DisplayName: !Sub ${AWS::StackName}-sns-topic + TopicName: !Sub ${AWS::StackName}-sns-topic + Subscription: + - Endpoint: !Ref ApproverEmailAddress + Protocol: "email" + Metadata: + SamResourceId: SNSNotificationTopic + + ReviewRESTApiRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: + - apigateway.amazonaws.com + Action: + - sts:AssumeRole + Policies: + - PolicyName: !Sub ${AWS::StackName}-sfn-permissions + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - states:SendTaskSuccess + Resource: "*" + Metadata: + SamResourceId: ReviewRESTApiRole + + ReviewRESTApi: + Type: AWS::Serverless::Api + Properties: + StageName: Prod + DefinitionBody: + Fn::Transform: + Name: AWS::Include + Parameters: + Location: ./APIGateway/ReviewRESTApi.yaml + Metadata: + SamResourceId: ReviewRESTApi + + ReviewSfnRole: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub ${AWS::StackName}-review-sfn-role + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: states.amazonaws.com + Action: sts:AssumeRole + Policies: + - PolicyName: !Sub ${AWS::StackName}-review-sfn-policy + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Action: + - sns:Publish + Resource: !GetAtt SNSNotificationTopic.TopicArn + Metadata: + SamResourceId: ReviewSfnRole + + ReviewStateMachine: + Type: AWS::Serverless::StateMachine + Properties: + DefinitionUri: "./StepFunction/step-function-definition.json" + DefinitionSubstitutions: + TopicArn: !GetAtt SNSNotificationTopic.TopicArn + ApiEndpoint: !Sub https://${ReviewRESTApi}.execute-api.${AWS::Region}.amazonaws.com/Prod + Role: !GetAtt ReviewSfnRole.Arn + Metadata: + SamResourceId: "ReviewStateMachine" +