AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Expense Approval Bot - Slack reaction-driven multi-stage approval router Parameters: SlackBotTokenSecretArn: Type: String Description: ARN of the Secrets Manager secret containing the Slack bot token (xoxb-...) SlackSigningSecretArn: Type: String Description: ARN of the Secrets Manager secret containing the Slack app signing secret Globals: Function: Runtime: python3.12 Timeout: 15 MemorySize: 256 Architectures: - arm64 Resources: ProcessorFunction: Type: AWS::Serverless::Function Properties: FunctionName: expense-approval-processor Handler: app.handler CodeUri: src/processor/ Environment: Variables: SLACK_BOT_TOKEN_SECRET_ARN: !Ref SlackBotTokenSecretArn Policies: - Version: '2012-10-17' Statement: - Effect: Allow Action: secretsmanager:GetSecretValue Resource: !Ref SlackBotTokenSecretArn ReceiverFunction: Type: AWS::Serverless::Function Properties: FunctionName: expense-approval-receiver Handler: app.handler CodeUri: src/receiver/ Timeout: 5 Environment: Variables: PROCESSOR_FUNCTION_NAME: !Ref ProcessorFunction SLACK_SIGNING_SECRET_ARN: !Ref SlackSigningSecretArn Policies: - Version: '2012-10-17' Statement: - Effect: Allow Action: lambda:InvokeFunction Resource: !GetAtt ProcessorFunction.Arn - Effect: Allow Action: secretsmanager:GetSecretValue Resource: !Ref SlackSigningSecretArn Events: SlackEvents: Type: HttpApi Properties: Path: /slack/events Method: POST Outputs: SlackEventsUrl: Description: Paste this into the Slack app's Event Subscriptions -> Request URL Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/slack/events" ProcessorFunctionArn: Value: !GetAtt ProcessorFunction.Arn ReceiverFunctionArn: Value: !GetAtt ReceiverFunction.Arn