AWSTemplateFormatVersion: '2010-09-09' Transform: AWS::Serverless-2016-10-31 Description: Payments Dashboard - S3 CSV ingestion to Slack App Home Globals: Function: Runtime: nodejs20.x 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 ProcessPaymentCsvFunction: Type: AWS::Serverless::Function Properties: FunctionName: payments-processPaymentCsv Handler: src/processPaymentCsv.handler 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 - 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://sandbox.cashpro.bankofamerica.com BOA_API_TOKEN_PARAM: /payments-dashboard/boa-api-token BOA_ACCOUNT_NUMBER_PARAM: /payments-dashboard/boa-account-number Events: DailySchedule: Type: Schedule Properties: Schedule: cron(0 13 ? * MON-FRI *) Description: Fetch BoA previous day transactions at 9am ET (13:00 UTC) Enabled: false Policies: - DynamoDBCrudPolicy: TableName: !Ref DashboardTable - SSMParameterReadPolicy: ParameterName: payments-dashboard/boa-api-token - SSMParameterReadPolicy: ParameterName: payments-dashboard/boa-account-number - 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