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 Resources: # 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 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: "*" 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