mirror of
https://github.com/Sea-Haven-Industries/payments-dashboard.git
synced 2026-09-30 07:43:12 +00:00
Queries Aurora for new payroll runs and contractor payments, sends formatted Slack message with gross pay breakdown and total bank withdrawal amount. Runs weekdays at 2pm ET, tracks notified payrolls in DynamoDB to avoid duplicates.
396 lines
12 KiB
YAML
396 lines
12 KiB
YAML
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
|
|
|
|
PublicSubnetB:
|
|
Type: AWS::EC2::Subnet
|
|
Properties:
|
|
VpcId: !Ref Vpc
|
|
CidrBlock: 10.20.3.0/24
|
|
AvailabilityZone: !Select [1, !GetAZs ""]
|
|
MapPublicIpOnLaunch: true
|
|
Tags:
|
|
- Key: Name
|
|
Value: payments-dashboard-public-b
|
|
|
|
PublicSubnetBRouteTableAssociation:
|
|
Type: AWS::EC2::SubnetRouteTableAssociation
|
|
Properties:
|
|
SubnetId: !Ref PublicSubnetB
|
|
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
|
|
|
|
# Aurora Serverless v2 PostgreSQL (Dataddo → payroll data)
|
|
AuroraSecurityGroup:
|
|
Type: AWS::EC2::SecurityGroup
|
|
Properties:
|
|
GroupDescription: Aurora PostgreSQL access
|
|
VpcId: !Ref Vpc
|
|
SecurityGroupIngress:
|
|
- IpProtocol: tcp
|
|
FromPort: 5432
|
|
ToPort: 5432
|
|
SourceSecurityGroupId: !Ref LambdaSecurityGroup
|
|
- IpProtocol: tcp
|
|
FromPort: 5432
|
|
ToPort: 5432
|
|
CidrIp: 0.0.0.0/0
|
|
|
|
AuroraSubnetGroup:
|
|
Type: AWS::RDS::DBSubnetGroup
|
|
Properties:
|
|
DBSubnetGroupDescription: Public subnets for Aurora PostgreSQL
|
|
SubnetIds:
|
|
- !Ref PublicSubnet
|
|
- !Ref PublicSubnetB
|
|
|
|
AuroraCluster:
|
|
Type: AWS::RDS::DBCluster
|
|
Properties:
|
|
Engine: aurora-postgresql
|
|
EngineVersion: "16.4"
|
|
DatabaseName: payroll
|
|
MasterUsername: payroll_admin
|
|
ManageMasterUserPassword: true
|
|
ServerlessV2ScalingConfiguration:
|
|
MinCapacity: 0.5
|
|
MaxCapacity: 2
|
|
VpcSecurityGroupIds:
|
|
- !Ref AuroraSecurityGroup
|
|
DBSubnetGroupName: !Ref AuroraSubnetGroup
|
|
EnableHttpEndpoint: true
|
|
StorageEncrypted: true
|
|
|
|
AuroraInstance:
|
|
Type: AWS::RDS::DBInstance
|
|
Properties:
|
|
DBClusterIdentifier: !Ref AuroraCluster
|
|
DBInstanceClass: db.serverless
|
|
Engine: aurora-postgresql
|
|
PubliclyAccessible: true
|
|
|
|
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
|
|
|
|
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: "*"
|
|
|
|
NotifyPayrollFunction:
|
|
Type: AWS::Serverless::Function
|
|
Properties:
|
|
FunctionName: payments-notifyPayroll
|
|
Handler: src/notifyPayroll.handler
|
|
Timeout: 60
|
|
VpcConfig:
|
|
SubnetIds:
|
|
- !Ref PrivateSubnet
|
|
SecurityGroupIds:
|
|
- !Ref LambdaSecurityGroup
|
|
Environment:
|
|
Variables:
|
|
AURORA_CLUSTER_ARN: !GetAtt AuroraCluster.DBClusterArn
|
|
AURORA_SECRET_ARN: !GetAtt AuroraCluster.MasterUserSecret.SecretArn
|
|
SLACK_BOT_TOKEN_PARAM: /payments-dashboard/slack-bot-token
|
|
Events:
|
|
PayrollCheck:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 18 ? * MON-FRI *)
|
|
Description: Check for new payroll data at 2pm ET (18:00 UTC)
|
|
Enabled: true
|
|
Policies:
|
|
- DynamoDBCrudPolicy:
|
|
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: "*"
|
|
- Effect: Allow
|
|
Action:
|
|
- rds-data:ExecuteStatement
|
|
Resource: !GetAtt AuroraCluster.DBClusterArn
|
|
- Effect: Allow
|
|
Action:
|
|
- secretsmanager:GetSecretValue
|
|
Resource: !GetAtt AuroraCluster.MasterUserSecret.SecretArn
|
|
|
|
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
|
|
AuroraEndpoint:
|
|
Description: Aurora PostgreSQL cluster endpoint
|
|
Value: !GetAtt AuroraCluster.Endpoint.Address
|
|
AuroraPort:
|
|
Description: Aurora PostgreSQL port
|
|
Value: !GetAtt AuroraCluster.Endpoint.Port
|
|
AuroraSecretArn:
|
|
Description: Secrets Manager ARN for Aurora master credentials
|
|
Value: !GetAtt AuroraCluster.MasterUserSecret.SecretArn
|