payments-dashboard/template.yaml

474 lines
15 KiB
YAML
Raw Normal View History

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Payments Dashboard - S3 CSV ingestion to Slack App Home
Globals:
Function:
Runtime: nodejs22.x
Architectures:
- arm64
Timeout: 30
MemorySize: 256
Environment:
Variables:
TABLE_NAME: !Ref DashboardTable
# Access logging + default throttling on the implicit HTTP API (audit M-18).
HttpApi:
AccessLogSettings:
DestinationArn: !GetAtt ApiAccessLogGroup.Arn
Format: '{"requestId":"$context.requestId","ip":"$context.identity.sourceIp","requestTime":"$context.requestTime","method":"$context.httpMethod","routeKey":"$context.routeKey","status":"$context.status","protocol":"$context.protocol","responseLength":"$context.responseLength","integrationError":"$context.integrationErrorMessage"}'
DefaultRouteSettings:
ThrottlingBurstLimit: 50
ThrottlingRateLimit: 100
Resources:
ApiAccessLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/apigateway/payments-dashboard
RetentionInDays: 90
# VPC with private subnet + NAT Gateway for static outbound IP
Vpc:
Type: AWS::EC2::VPC
Properties:
CidrBlock: 10.20.0.0/16
EnableDnsSupport: true
EnableDnsHostnames: true
Tags:
- Key: Name
Value: payments-dashboard-vpc
PrivateSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: 10.20.1.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- Key: Name
Value: payments-dashboard-private
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref Vpc
CidrBlock: 10.20.2.0/24
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- Key: Name
Value: payments-dashboard-public
InternetGateway:
Type: AWS::EC2::InternetGateway
VpcGatewayAttachment:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref Vpc
InternetGatewayId: !Ref InternetGateway
NatEip:
Type: AWS::EC2::EIP
Properties:
Domain: vpc
NatGateway:
Type: AWS::EC2::NatGateway
Properties:
AllocationId: !GetAtt NatEip.AllocationId
SubnetId: !Ref PublicSubnet
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
PublicRoute:
Type: AWS::EC2::Route
DependsOn: VpcGatewayAttachment
Properties:
RouteTableId: !Ref PublicRouteTable
DestinationCidrBlock: 0.0.0.0/0
GatewayId: !Ref InternetGateway
PublicSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PublicSubnet
RouteTableId: !Ref PublicRouteTable
PrivateRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref Vpc
PrivateRoute:
Type: AWS::EC2::Route
Properties:
RouteTableId: !Ref PrivateRouteTable
DestinationCidrBlock: 0.0.0.0/0
NatGatewayId: !Ref NatGateway
PrivateSubnetRouteTableAssociation:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
SubnetId: !Ref PrivateSubnet
RouteTableId: !Ref PrivateRouteTable
LambdaSecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Payments Dashboard Lambda outbound access
VpcId: !Ref Vpc
SecurityGroupEgress:
- IpProtocol: "-1"
CidrIp: 0.0.0.0/0
PaymentsCsvBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub seahaven-payments-csv-${AWS::AccountId}
DashboardTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: PaymentsDashboard
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: pk
AttributeType: S
KeySchema:
- AttributeName: pk
KeyType: HASH
TimeToLiveSpecification:
AttributeName: ttl
Enabled: true
PayrollEmailBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub seahaven-payroll-emails-${AWS::AccountId}
LifecycleConfiguration:
Rules:
- Id: ExpireEmails
Status: Enabled
ExpirationInDays: 30
PayrollEmailBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref PayrollEmailBucket
PolicyDocument:
Version: "2012-10-17"
Statement:
- Sid: AllowSESPut
Effect: Allow
Principal:
Service: ses.amazonaws.com
Action: s3:PutObject
Resource: !Sub arn:aws:s3:::seahaven-payroll-emails-${AWS::AccountId}/*
Condition:
StringEquals:
AWS:SourceAccount: !Ref AWS::AccountId
PayrollEmailRule:
Type: AWS::SES::ReceiptRule
DependsOn: PayrollEmailBucketPolicy
Properties:
RuleSetName: INBOUND_MAIL
After: ExistingRuleSetWorkorderEmailRuleEA29F845-okepxcVarTfu
Rule:
Name: store-payroll-emails
Enabled: true
ScanEnabled: true
Recipients:
- payroll@int.seahaven.com
Actions:
- S3Action:
BucketName: !Ref PayrollEmailBucket
ObjectKeyPrefix: inbound/
PayrollBatchQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: payments-payroll-batch
DelaySeconds: 600
MessageRetentionPeriod: 86400
VisibilityTimeout: 60
ProcessPayrollEmailLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/payments-processPayrollEmail
RetentionInDays: 60
ProcessPaymentCsvLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/payments-processPaymentCsv
RetentionInDays: 60
SlackAppHomeLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/payments-slackAppHome
RetentionInDays: 60
FetchBoaTransactionsLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/payments-fetchBoaTransactions
RetentionInDays: 60
ExpenseReceiverLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/payments-expenseReceiver
RetentionInDays: 60
ExpenseProcessorLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /aws/lambda/payments-expenseProcessor
RetentionInDays: 60
ProcessPayrollEmailFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-processPayrollEmail
Handler: src/processPayrollEmail.handler
Timeout: 60
Environment:
Variables:
SLACK_BOT_TOKEN_PARAM: /payments-dashboard/slack-bot-token
PAYROLL_CHANNEL_ID: C0AV5RBMYKU
PAYROLL_BATCH_QUEUE_URL: !Ref PayrollBatchQueue
Events:
EmailReceived:
Type: S3
Properties:
Bucket: !Ref PayrollEmailBucket
Events: s3:ObjectCreated:*
Filter:
S3Key:
Rules:
- Name: prefix
Value: inbound/
PayrollBatch:
Type: SQS
Properties:
Queue: !GetAtt PayrollBatchQueue.Arn
BatchSize: 1
Policies:
- S3ReadPolicy:
BucketName: !Sub seahaven-payroll-emails-${AWS::AccountId}
- DynamoDBCrudPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/slack-bot-token
- SQSSendMessagePolicy:
QueueName: !GetAtt PayrollBatchQueue.QueueName
- SQSPollerPolicy:
QueueName: !GetAtt PayrollBatchQueue.QueueName
ProcessPaymentCsvFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-processPaymentCsv
Handler: src/processPaymentCsv.handler
Timeout: 120
Environment:
Variables:
BOA_BASE_URL: https://api.bofa.com
BOA_CHECK_MGMT_APP_ID_PARAM: /payments-dashboard/boa-check-mgmt-app-id
BOA_CHECK_MGMT_CLIENT_ID_PARAM: /payments-dashboard/boa-check-mgmt-client-id
BOA_CHECK_MGMT_SECRET_PARAM: /payments-dashboard/boa-check-mgmt-token
BOA_ACCOUNT_NUMBER_PARAM: /payments-dashboard/boa-account-number
BOA_COMPANY_ID_PARAM: /payments-dashboard/boa-company-id
VpcConfig:
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref LambdaSecurityGroup
Events:
CsvUpload:
Type: S3
Properties:
Bucket: !Ref PaymentsCsvBucket
Events: s3:ObjectCreated:*
Filter:
S3Key:
Rules:
- Name: suffix
Value: .csv
Policies:
- S3ReadPolicy:
BucketName: !Sub seahaven-payments-csv-${AWS::AccountId}
- DynamoDBCrudPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-app-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-client-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-check-mgmt-token
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-number
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-company-id
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DeleteNetworkInterface
Resource: "*"
SlackAppHomeFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-slackAppHome
Handler: src/slackAppHome.handler
VpcConfig:
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref LambdaSecurityGroup
Environment:
Variables:
SLACK_BOT_TOKEN_PARAM: /payments-dashboard/slack-bot-token
Events:
SlackEvent:
Type: HttpApi
Properties:
Path: /slack/events
Method: POST
Policies:
- DynamoDBReadPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/slack-bot-token
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DeleteNetworkInterface
Resource: "*"
FetchBoaTransactionsFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-fetchBoaTransactions
Handler: src/fetchBoaTransactions.handler
Timeout: 60
VpcConfig:
SubnetIds:
- !Ref PrivateSubnet
SecurityGroupIds:
- !Ref LambdaSecurityGroup
Environment:
Variables:
BOA_BASE_URL: https://api.bofa.com
BOA_REPORTING_APP_ID_PARAM: /payments-dashboard/boa-reporting-app-id
BOA_REPORTING_CLIENT_ID_PARAM: /payments-dashboard/boa-account-info-client-id
BOA_REPORTING_SECRET_PARAM: /payments-dashboard/boa-account-info-token
BOA_ACCOUNT_NUMBER_PARAM: /payments-dashboard/boa-account-number
BOA_BANK_ID_PARAM: /payments-dashboard/boa-bank-id
Events:
DailySchedule:
Type: Schedule
Properties:
Schedule: cron(0 13 ? * MON-FRI *)
Description: Fetch BoA previous day transactions at 9am ET (13:00 UTC)
Enabled: true
Policies:
- DynamoDBCrudPolicy:
TableName: !Ref DashboardTable
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-reporting-app-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-info-client-id
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-info-token
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-account-number
- SSMParameterReadPolicy:
ParameterName: payments-dashboard/boa-bank-id
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- ec2:CreateNetworkInterface
- ec2:DescribeNetworkInterfaces
- ec2:DeleteNetworkInterface
Resource: "*"
ExpenseProcessorFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-expenseProcessor
Handler: src/expenseProcessor.handler
Timeout: 15
Environment:
Variables:
EXPENSE_BOT_TOKEN_SECRET_NAME: payments-dashboard/expense-slack-token
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:payments-dashboard/expense-slack-token-*
ExpenseReceiverFunction:
Type: AWS::Serverless::Function
Properties:
FunctionName: payments-expenseReceiver
Handler: src/expenseReceiver.handler
Timeout: 5
Environment:
Variables:
EXPENSE_PROCESSOR_FN: !Ref ExpenseProcessorFunction
EXPENSE_SIGNING_SECRET_NAME: payments-dashboard/expense-slack-signing-secret
Events:
ExpenseSlackEvent:
Type: HttpApi
Properties:
Path: /slack/expense-events
Method: POST
Policies:
- Version: "2012-10-17"
Statement:
- Effect: Allow
Action: lambda:InvokeFunction
Resource: !GetAtt ExpenseProcessorFunction.Arn
- Effect: Allow
Action: secretsmanager:GetSecretValue
Resource: !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:payments-dashboard/expense-slack-signing-secret-*
Outputs:
SlackEventUrl:
Description: URL to set as the Slack app Request URL
Value: !Sub https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/slack/events
CsvBucket:
Description: S3 bucket for CSV uploads
Value: !Ref PaymentsCsvBucket
StaticOutboundIp:
Description: Static IP for BoA API whitelist
Value: !Ref NatEip
PayrollEmailBucket:
Description: S3 bucket for inbound payroll emails from SES
Value: !Ref PayrollEmailBucket
ExpenseSlackEventsUrl:
Description: URL for Expense Approval Bot Slack Event Subscriptions
Value: !Sub https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/slack/expense-events
ExpenseProcessorFunctionArn:
Description: Expense Processor Lambda ARN
Value: !GetAtt ExpenseProcessorFunction.Arn
ExpenseReceiverFunctionArn:
Description: Expense Receiver Lambda ARN
Value: !GetAtt ExpenseReceiverFunction.Arn