Bereitstellen eines Beispielprojekts für Genehmigungen durch Menschen - AWS Step Functions

Die vorliegende Übersetzung wurde maschinell erstellt. Im Falle eines Konflikts oder eines Widerspruchs zwischen dieser übersetzten Fassung und der englischen Fassung (einschließlich infolge von Verzögerungen bei der Übersetzung) ist die englische Fassung maßgeblich.

Bereitstellen eines Beispielprojekts für Genehmigungen durch Menschen

In diesem Tutorial erfahren Sie, wie Sie ein Projekt mit Genehmigungen durch Menschen bereitstellen, das das Anhalten einer AWS Step Functions-Ausführung während einer Aufgabe und das Warten auf die E-Mail-Antwort eines Benutzers ermöglicht. Der Workflow wird mit der nächsten Phase fortgesetzt, sobald der Benutzer die Aufgabe genehmigt hat.

Durch das Bereitstellen des AWS CloudFormation-Stacks in diesem Tutorial werden alle erforderlichen Ressourcen erstellt, einschließlich:

  • Amazon API Gateway Gateway-Ressourcen

  • Eine AWS Lambda-Funktion

  • Ein AWS Step Functions-Zustandsautomat

  • Ein Amazon Simple Notification Service--Thema

  • Verwandte AWS Identity and Access Management-Rollen und -Berechtigungen

Anmerkung

Sie müssen eine gültige E-Mail-Adresse bereitstellen, auf die Sie zugreifen können, wenn Sie den AWS CloudFormation-Stack erstellen.

Weitere Informationen finden Sie unterArbeiten mit CloudFormation -Vorlagenund dieAWS::StepFunctions::StateMachineressource in derAWS CloudFormationBenutzerhandbuch.

Schritt 1: Erstellen einesAWS CloudFormation-Vorlage

  1. Kopieren Sie den Beispielcode aus dem Abschnitt AWS CloudFormation-Vorlagenquellcode.

  2. Fügen Sie die Quelle der AWS CloudFormation-Vorlage in eine Datei auf Ihrem lokalen Computer ein.

    In diesem Beispiel hat die Datei den Namen human-approval.yaml.

Schritt 2: Erstellen eines Stacks

  1. Melden Sie sich an der AWS CloudFormation-Konsole an.

  2. Klicken Sie aufCreate Stack (Stapel erstellen)und anschließendMit neuen Ressourcen (standard).

  3. Auf derErstellen eines Stacks, machen Sie das Folgende:

    1. In derVoraussetzung — Vorlage vorbereiten-Bereich, stellen SieVorlage ist bereitist ausgewählt.

    2. In derSpecify-Bereich, wählen SieHochladen einer Vorlagendabeiund danachWählen Sie Dateium dashuman-approval.yamlDatei, die Sie zuvor erstellt haben und dieVorlagen-Quellcode.

  4. Wählen Sie Next (Weiter).

  5. Führen Sie auf der Seite Specify DB Details (Festlegen von DB-Detail) die folgenden Schritte aus:

    1. FürStack name, geben Sie einen Namen für Ihren Stack ein.

    2. UnderParameter, geben Sie eine gültige E-Mail-Adresse ein. Sie verwenden diese E-Mail-Adresse, um das Amazon-SNS-Thema

  6. Klicken Sie aufWeiterund anschließendWeiter.

  7. Auf derPrüfen-Seite, wählen SieIch erkenne das anAWS CloudFormationkönnte IAM-Ressourcen erstellenund danachGeben Sie einen Namen für den Benutzer ein und klicken Sie dann auf.

    AWS CloudFormation beginnt mit dem Erstellen des Stacks und zeigt den Status CREATE_IN_PROGRESS (ERSTELLEN_IN_BEARBEITUNG) an. Wenn der Prozess abgeschlossen ist, zeigt AWS CloudFormation den Status CREATE_COMPLETE an.

  8. (Optional) Wählen Sie zum Anzeigen der Ressourcen in Ihrem Stack den Stack und anschließend die Registerkarte Resources aus.

    
            Anzeige von Ressourcen

Schritt 3: Genehmigen Amazon SNS

Sobald das Amazon-SNS-Thema erstellt wurde, erhalten Sie eine E-Mail, in der Sie aufgefordert werden, das Abonnement zu bestätigen.

  1. Öffnen Sie das E-Mail-Konto, das Sie beim Erstellen des AWS CloudFormation-Stacks angegeben haben.

  2. Öffne die NachrichtAWSBenachrichtigung — Bestätigung des Abonnementsvonno-reply@sns.amazonaws.com

    Die E-Mail enthält den Amazon-Ressourcennamen und einen Bestätigungs-Link.

  3. Wählen Sie den Link confirm subscription (Abonnement bestätigen) aus.

    
            Abonnement bestätigt!

Schritt 4: Ausführen einer Ausführung

  1. Melden Sie sich beimStep Functions Konsole.

  2. Auf derZustandsautomaten-Seite, wählen SieHumanApprovalLambdaStateMachine.

  3. Wählen Sie Start execution (Ausführung starten) aus.

  4. Geben Sie einen Namen für die Ausführung ein, z. B. ApprovalTest.

  5. (Optional) Geben Sie eine Eingabe für die Ausführung ein.

    { "Comment": "Testing the human approval tutorial." }
  6. Wählen Sie Start execution (Ausführung starten) aus.

    DieApprovalTestDie Ausführung der Zustandsmaschine wird gestartet und pausiert amLambda RückrufAufgabe.

    
            Ausführung, die auf Callback wartet
  7. Öffnen Sie in dem E-Mail-Konto, das Sie zuvor für das Amazon-SNS-Thema verwendet haben, die Nachricht mit dem BetreffErforderliche Genehmigung vonAWS Step Functions.

    Die Nachricht enthält getrennte URLs für Approve (Genehmigen) und Reject (Ablehnen).

  8. Wählen Sie die URL für Approve (Genehmigen) aus.

    Der Workflow wird abhängig von Ihrer Wahl fortgesetzt.

    
            Ausführung, die auf Callback wartet

AWS CloudFormation-Vorlagenquellcode

Verwenden Sie diese AWS CloudFormation-Vorlage zum Bereitstellen eines Beispiels für einen Workflow mit Genehmigungen durch Menschen.

AWSTemplateFormatVersion: "2010-09-09" Description: "AWS Step Functions Human based task example. It sends an email with an HTTP URL for approval." Parameters: Email: Type: String AllowedPattern: "^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\\.[a-zA-Z0-9-.]+$" ConstraintDescription: Must be a valid email address. Resources: # Begin API Gateway Resources ExecutionApi: Type: "AWS::ApiGateway::RestApi" Properties: Name: "Human approval endpoint" Description: "HTTP Endpoint backed by API Gateway and Lambda" FailOnWarnings: true ExecutionResource: Type: 'AWS::ApiGateway::Resource' Properties: RestApiId: !Ref ExecutionApi ParentId: !GetAtt "ExecutionApi.RootResourceId" PathPart: execution ExecutionMethod: Type: "AWS::ApiGateway::Method" Properties: AuthorizationType: NONE HttpMethod: GET Integration: Type: AWS IntegrationHttpMethod: POST Uri: !Sub "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaApprovalFunction.Arn}/invocations" IntegrationResponses: - StatusCode: 302 ResponseParameters: method.response.header.Location: "integration.response.body.headers.Location" RequestTemplates: application/json: | { "body" : $input.json('$'), "headers": { #foreach($header in $input.params().header.keySet()) "$header": "$util.escapeJavaScript($input.params().header.get($header))" #if($foreach.hasNext),#end #end }, "method": "$context.httpMethod", "params": { #foreach($param in $input.params().path.keySet()) "$param": "$util.escapeJavaScript($input.params().path.get($param))" #if($foreach.hasNext),#end #end }, "query": { #foreach($queryParam in $input.params().querystring.keySet()) "$queryParam": "$util.escapeJavaScript($input.params().querystring.get($queryParam))" #if($foreach.hasNext),#end #end } } ResourceId: !Ref ExecutionResource RestApiId: !Ref ExecutionApi MethodResponses: - StatusCode: 302 ResponseParameters: method.response.header.Location: true ApiGatewayAccount: Type: 'AWS::ApiGateway::Account' Properties: CloudWatchRoleArn: !GetAtt "ApiGatewayCloudWatchLogsRole.Arn" ApiGatewayCloudWatchLogsRole: Type: 'AWS::IAM::Role' Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - apigateway.amazonaws.com Action: - 'sts:AssumeRole' Policies: - PolicyName: ApiGatewayLogsPolicy PolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Action: - "logs:*" Resource: !Sub "arn:${AWS::Partition}:logs:*:*:*" ExecutionApiStage: DependsOn: - ApiGatewayAccount Type: 'AWS::ApiGateway::Stage' Properties: DeploymentId: !Ref ApiDeployment MethodSettings: - DataTraceEnabled: true HttpMethod: '*' LoggingLevel: INFO ResourcePath: /* RestApiId: !Ref ExecutionApi StageName: states ApiDeployment: Type: "AWS::ApiGateway::Deployment" DependsOn: - ExecutionMethod Properties: RestApiId: !Ref ExecutionApi StageName: DummyStage # End API Gateway Resources # Begin # Lambda that will be invoked by API Gateway LambdaApprovalFunction: Type: 'AWS::Lambda::Function' Properties: Code: ZipFile: Fn::Sub: | const AWS = require('aws-sdk'); var redirectToStepFunctions = function(lambdaArn, statemachineName, executionName, callback) { const lambdaArnTokens = lambdaArn.split(":"); const partition = lambdaArnTokens[1]; const region = lambdaArnTokens[3]; const accountId = lambdaArnTokens[4]; console.log("partition=" + partition); console.log("region=" + region); console.log("accountId=" + accountId); const executionArn = "arn:" + partition + ":states:" + region + ":" + accountId + ":execution:" + statemachineName + ":" + executionName; console.log("executionArn=" + executionArn); const url = "https://console.aws.amazon.com/states/home?region=" + region + "#/executions/details/" + executionArn; callback(null, { statusCode: 302, headers: { Location: url } }); }; exports.handler = (event, context, callback) => { console.log('Event= ' + JSON.stringify(event)); const action = event.query.action; const taskToken = event.query.taskToken; const statemachineName = event.query.sm; const executionName = event.query.ex; const stepfunctions = new AWS.StepFunctions(); var message = ""; if (action === "approve") { message = { "Status": "Approved! Task approved by ${Email}" }; } else if (action === "reject") { message = { "Status": "Rejected! Task rejected by ${Email}" }; } else { console.error("Unrecognized action. Expected: approve, reject."); callback({"Status": "Failed to process the request. Unrecognized Action."}); } stepfunctions.sendTaskSuccess({ output: JSON.stringify(message), taskToken: event.query.taskToken }) .promise() .then(function(data) { redirectToStepFunctions(context.invokedFunctionArn, statemachineName, executionName, callback); }).catch(function(err) { console.error(err, err.stack); callback(err); }); } Description: Lambda function that callback to AWS Step Functions FunctionName: LambdaApprovalFunction Handler: index.handler Role: !GetAtt "LambdaApiGatewayIAMRole.Arn" Runtime: nodejs12.x LambdaApiGatewayInvoke: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt "LambdaApprovalFunction.Arn" Principal: "apigateway.amazonaws.com" SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ExecutionApi}/*" LambdaApiGatewayIAMRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Action: - "sts:AssumeRole" Effect: "Allow" Principal: Service: - "lambda.amazonaws.com" Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Statement: - Effect: Allow Action: - "logs:*" Resource: !Sub "arn:${AWS::Partition}:logs:*:*:*" - PolicyName: StepFunctionsPolicy PolicyDocument: Statement: - Effect: Allow Action: - "states:SendTaskFailure" - "states:SendTaskSuccess" Resource: "*" # End Lambda that will be invoked by API Gateway # Begin state machine that publishes to Lambda and sends an email with the link for approval HumanApprovalLambdaStateMachine: Type: AWS::StepFunctions::StateMachine Properties: RoleArn: !GetAtt LambdaStateMachineExecutionRole.Arn DefinitionString: Fn::Sub: | { "StartAt": "Lambda Callback", "TimeoutSeconds": 3600, "States": { "Lambda Callback": { "Type": "Task", "Resource": "arn:${AWS::Partition}:states:::lambda:invoke.waitForTaskToken", "Parameters": { "FunctionName": "${LambdaHumanApprovalSendEmailFunction.Arn}", "Payload": { "ExecutionContext.$": "$$", "APIGatewayEndpoint": "https://${ExecutionApi}.execute-api.${AWS::Region}.amazonaws.com/states" } }, "Next": "ManualApprovalChoiceState" }, "ManualApprovalChoiceState": { "Type": "Choice", "Choices": [ { "Variable": "$.Status", "StringEquals": "Approved! Task approved by ${Email}", "Next": "ApprovedPassState" }, { "Variable": "$.Status", "StringEquals": "Rejected! Task rejected by ${Email}", "Next": "RejectedPassState" } ] }, "ApprovedPassState": { "Type": "Pass", "End": true }, "RejectedPassState": { "Type": "Pass", "End": true } } } SNSHumanApprovalEmailTopic: Type: AWS::SNS::Topic Properties: Subscription: - Endpoint: !Sub ${Email} Protocol: email LambdaHumanApprovalSendEmailFunction: Type: "AWS::Lambda::Function" Properties: Handler: "index.lambda_handler" Role: !GetAtt LambdaSendEmailExecutionRole.Arn Runtime: "nodejs12.x" Timeout: "25" Code: ZipFile: Fn::Sub: | console.log('Loading function'); const AWS = require('aws-sdk'); exports.lambda_handler = (event, context, callback) => { console.log('event= ' + JSON.stringify(event)); console.log('context= ' + JSON.stringify(context)); const executionContext = event.ExecutionContext; console.log('executionContext= ' + executionContext); const executionName = executionContext.Execution.Name; console.log('executionName= ' + executionName); const statemachineName = executionContext.StateMachine.Name; console.log('statemachineName= ' + statemachineName); const taskToken = executionContext.Task.Token; console.log('taskToken= ' + taskToken); const apigwEndpint = event.APIGatewayEndpoint; console.log('apigwEndpint = ' + apigwEndpint) const approveEndpoint = apigwEndpint + "/execution?action=approve&ex=" + executionName + "&sm=" + statemachineName + "&taskToken=" + encodeURIComponent(taskToken); console.log('approveEndpoint= ' + approveEndpoint); const rejectEndpoint = apigwEndpint + "/execution?action=reject&ex=" + executionName + "&sm=" + statemachineName + "&taskToken=" + encodeURIComponent(taskToken); console.log('rejectEndpoint= ' + rejectEndpoint); const emailSnsTopic = "${SNSHumanApprovalEmailTopic}"; console.log('emailSnsTopic= ' + emailSnsTopic); var emailMessage = 'Welcome! \n\n'; emailMessage += 'This is an email requiring an approval for a step functions execution. \n\n' emailMessage += 'Please check the following information and click "Approve" link if you want to approve. \n\n' emailMessage += 'Execution Name -> ' + executionName + '\n\n' emailMessage += 'Approve ' + approveEndpoint + '\n\n' emailMessage += 'Reject ' + rejectEndpoint + '\n\n' emailMessage += 'Thanks for using Step functions!' const sns = new AWS.SNS(); var params = { Message: emailMessage, Subject: "Required approval from AWS Step Functions", TopicArn: emailSnsTopic }; sns.publish(params) .promise() .then(function(data) { console.log("MessageID is " + data.MessageId); callback(null); }).catch( function(err) { console.error(err, err.stack); callback(err); }); } LambdaStateMachineExecutionRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: states.amazonaws.com Action: "sts:AssumeRole" Policies: - PolicyName: InvokeCallbackLambda PolicyDocument: Statement: - Effect: Allow Action: - "lambda:InvokeFunction" Resource: - !Sub "${LambdaHumanApprovalSendEmailFunction.Arn}" LambdaSendEmailExecutionRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: "sts:AssumeRole" Policies: - PolicyName: CloudWatchLogsPolicy PolicyDocument: Statement: - Effect: Allow Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: !Sub "arn:${AWS::Partition}:logs:*:*:*" - PolicyName: SNSSendEmailPolicy PolicyDocument: Statement: - Effect: Allow Action: - "SNS:Publish" Resource: - !Sub "${SNSHumanApprovalEmailTopic}" # End state machine that publishes to Lambda and sends an email with the link for approval Outputs: ApiGatewayInvokeURL: Value: !Sub "https://${ExecutionApi}.execute-api.${AWS::Region}.amazonaws.com/states" StateMachineHumanApprovalArn: Value: !Ref HumanApprovalLambdaStateMachine