398 lines
12 KiB
YAML
398 lines
12 KiB
YAML
|
|
AWSTemplateFormatVersion: '2010-09-09'
|
||
|
|
Transform: AWS::Serverless-2016-10-31
|
||
|
|
Description: >
|
||
|
|
exec-aide — Gmail inbox monitor with AI classification and Slack alerts
|
||
|
|
|
||
|
|
Globals:
|
||
|
|
Function:
|
||
|
|
Runtime: python3.12
|
||
|
|
Architectures:
|
||
|
|
- arm64
|
||
|
|
MemorySize: 256
|
||
|
|
Timeout: 120
|
||
|
|
Environment:
|
||
|
|
Variables:
|
||
|
|
TABLE_NAME: !Ref ExecAideTable
|
||
|
|
SECRET_GMAIL: exec-aide/gmail-oauth
|
||
|
|
SECRET_SLACK: exec-aide/slack-credentials
|
||
|
|
SSM_PREFIX: /exec-aide
|
||
|
|
|
||
|
|
Resources:
|
||
|
|
|
||
|
|
# ── DynamoDB ──────────────────────────────────────────────
|
||
|
|
|
||
|
|
ExecAideTable:
|
||
|
|
Type: AWS::DynamoDB::Table
|
||
|
|
Properties:
|
||
|
|
TableName: exec-aide
|
||
|
|
BillingMode: PAY_PER_REQUEST
|
||
|
|
AttributeDefinitions:
|
||
|
|
- AttributeName: pk
|
||
|
|
AttributeType: S
|
||
|
|
- AttributeName: sk
|
||
|
|
AttributeType: S
|
||
|
|
- AttributeName: classified_date
|
||
|
|
AttributeType: S
|
||
|
|
KeySchema:
|
||
|
|
- AttributeName: pk
|
||
|
|
KeyType: HASH
|
||
|
|
- AttributeName: sk
|
||
|
|
KeyType: RANGE
|
||
|
|
GlobalSecondaryIndexes:
|
||
|
|
- IndexName: by-date
|
||
|
|
KeySchema:
|
||
|
|
- AttributeName: classified_date
|
||
|
|
KeyType: HASH
|
||
|
|
- AttributeName: sk
|
||
|
|
KeyType: RANGE
|
||
|
|
Projection:
|
||
|
|
ProjectionType: ALL
|
||
|
|
TimeToLiveSpecification:
|
||
|
|
AttributeName: ttl
|
||
|
|
Enabled: true
|
||
|
|
|
||
|
|
# ── CloudWatch Log Groups ────────────────────────────────
|
||
|
|
|
||
|
|
FetchClassifyLogGroup:
|
||
|
|
Type: AWS::Logs::LogGroup
|
||
|
|
Properties:
|
||
|
|
LogGroupName: /aws/lambda/exec-aide-fetch-classify
|
||
|
|
RetentionInDays: 60
|
||
|
|
|
||
|
|
DailyDigestLogGroup:
|
||
|
|
Type: AWS::Logs::LogGroup
|
||
|
|
Properties:
|
||
|
|
LogGroupName: /aws/lambda/exec-aide-daily-digest
|
||
|
|
RetentionInDays: 60
|
||
|
|
|
||
|
|
# ── Lambda Functions ─────────────────────────────────────
|
||
|
|
|
||
|
|
FetchClassifyFunction:
|
||
|
|
Type: AWS::Serverless::Function
|
||
|
|
DependsOn: FetchClassifyLogGroup
|
||
|
|
Properties:
|
||
|
|
FunctionName: exec-aide-fetch-classify
|
||
|
|
Handler: fetch_classify.app.lambda_handler
|
||
|
|
CodeUri: src/
|
||
|
|
Events:
|
||
|
|
PollSchedule:
|
||
|
|
Type: Schedule
|
||
|
|
Properties:
|
||
|
|
Schedule: rate(15 minutes)
|
||
|
|
Description: Poll Gmail for new messages
|
||
|
|
Enabled: true
|
||
|
|
Policies:
|
||
|
|
- DynamoDBCrudPolicy:
|
||
|
|
TableName: !Ref ExecAideTable
|
||
|
|
- Statement:
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- secretsmanager:GetSecretValue
|
||
|
|
Resource:
|
||
|
|
- !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:exec-aide/gmail-oauth-*
|
||
|
|
- !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:exec-aide/slack-credentials-*
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- secretsmanager:PutSecretValue
|
||
|
|
Resource:
|
||
|
|
- !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:exec-aide/gmail-oauth-*
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- ssm:GetParametersByPath
|
||
|
|
- ssm:GetParameter
|
||
|
|
Resource:
|
||
|
|
- !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/exec-aide/*
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- bedrock:InvokeModel
|
||
|
|
Resource:
|
||
|
|
- arn:aws:bedrock:*::foundation-model/anthropic.*
|
||
|
|
- !Sub arn:aws:bedrock:${AWS::Region}:${AWS::AccountId}:inference-profile/us.anthropic.*
|
||
|
|
|
||
|
|
DailyDigestFunction:
|
||
|
|
Type: AWS::Serverless::Function
|
||
|
|
DependsOn: DailyDigestLogGroup
|
||
|
|
Properties:
|
||
|
|
FunctionName: exec-aide-daily-digest
|
||
|
|
Handler: daily_digest.app.lambda_handler
|
||
|
|
CodeUri: src/
|
||
|
|
Policies:
|
||
|
|
- DynamoDBCrudPolicy:
|
||
|
|
TableName: !Ref ExecAideTable
|
||
|
|
- Statement:
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- secretsmanager:GetSecretValue
|
||
|
|
Resource:
|
||
|
|
- !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:exec-aide/gmail-oauth-*
|
||
|
|
- !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:exec-aide/slack-credentials-*
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- secretsmanager:PutSecretValue
|
||
|
|
Resource:
|
||
|
|
- !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:exec-aide/gmail-oauth-*
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- ssm:GetParametersByPath
|
||
|
|
- ssm:GetParameter
|
||
|
|
Resource:
|
||
|
|
- !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/exec-aide/*
|
||
|
|
|
||
|
|
# ── ECS / Fargate (Socket Mode listener) ──────────────────
|
||
|
|
|
||
|
|
ListenerLogGroup:
|
||
|
|
Type: AWS::Logs::LogGroup
|
||
|
|
Properties:
|
||
|
|
LogGroupName: /ecs/exec-aide-listener
|
||
|
|
RetentionInDays: 60
|
||
|
|
|
||
|
|
EcsCluster:
|
||
|
|
Type: AWS::ECS::Cluster
|
||
|
|
Properties:
|
||
|
|
ClusterName: exec-aide
|
||
|
|
|
||
|
|
ListenerTaskDefinition:
|
||
|
|
Type: AWS::ECS::TaskDefinition
|
||
|
|
Properties:
|
||
|
|
Family: exec-aide-listener
|
||
|
|
Cpu: "256"
|
||
|
|
Memory: "512"
|
||
|
|
NetworkMode: awsvpc
|
||
|
|
RequiresCompatibilities:
|
||
|
|
- FARGATE
|
||
|
|
RuntimePlatform:
|
||
|
|
CpuArchitecture: ARM64
|
||
|
|
OperatingSystemFamily: LINUX
|
||
|
|
ExecutionRoleArn: !GetAtt ListenerExecutionRole.Arn
|
||
|
|
TaskRoleArn: !GetAtt ListenerTaskRole.Arn
|
||
|
|
ContainerDefinitions:
|
||
|
|
- Name: listener
|
||
|
|
Image: !Sub ${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/exec-aide-listener:latest
|
||
|
|
Essential: true
|
||
|
|
Environment:
|
||
|
|
- Name: TABLE_NAME
|
||
|
|
Value: !Ref ExecAideTable
|
||
|
|
- Name: SECRET_SLACK
|
||
|
|
Value: exec-aide/slack-credentials
|
||
|
|
- Name: SSM_PREFIX
|
||
|
|
Value: /exec-aide
|
||
|
|
LogConfiguration:
|
||
|
|
LogDriver: awslogs
|
||
|
|
Options:
|
||
|
|
awslogs-group: /ecs/exec-aide-listener
|
||
|
|
awslogs-region: !Ref AWS::Region
|
||
|
|
awslogs-stream-prefix: listener
|
||
|
|
|
||
|
|
ListenerService:
|
||
|
|
Type: AWS::ECS::Service
|
||
|
|
Properties:
|
||
|
|
ServiceName: exec-aide-listener
|
||
|
|
Cluster: !Ref EcsCluster
|
||
|
|
TaskDefinition: !Ref ListenerTaskDefinition
|
||
|
|
DesiredCount: 1
|
||
|
|
LaunchType: FARGATE
|
||
|
|
NetworkConfiguration:
|
||
|
|
AwsvpcConfiguration:
|
||
|
|
AssignPublicIp: ENABLED
|
||
|
|
Subnets:
|
||
|
|
- !Ref ListenerSubnet
|
||
|
|
SecurityGroups:
|
||
|
|
- !Ref ListenerSecurityGroup
|
||
|
|
|
||
|
|
ListenerSubnet:
|
||
|
|
Type: AWS::EC2::Subnet
|
||
|
|
Properties:
|
||
|
|
VpcId: !Ref ListenerVpc
|
||
|
|
CidrBlock: 10.30.0.0/24
|
||
|
|
MapPublicIpOnLaunch: true
|
||
|
|
AvailabilityZone: !Select [0, !GetAZs ""]
|
||
|
|
Tags:
|
||
|
|
- Key: Name
|
||
|
|
Value: exec-aide-listener
|
||
|
|
|
||
|
|
ListenerVpc:
|
||
|
|
Type: AWS::EC2::VPC
|
||
|
|
Properties:
|
||
|
|
CidrBlock: 10.30.0.0/16
|
||
|
|
EnableDnsHostnames: true
|
||
|
|
EnableDnsSupport: true
|
||
|
|
Tags:
|
||
|
|
- Key: Name
|
||
|
|
Value: exec-aide
|
||
|
|
|
||
|
|
ListenerIgw:
|
||
|
|
Type: AWS::EC2::InternetGateway
|
||
|
|
Properties:
|
||
|
|
Tags:
|
||
|
|
- Key: Name
|
||
|
|
Value: exec-aide
|
||
|
|
|
||
|
|
ListenerIgwAttachment:
|
||
|
|
Type: AWS::EC2::VPCGatewayAttachment
|
||
|
|
Properties:
|
||
|
|
VpcId: !Ref ListenerVpc
|
||
|
|
InternetGatewayId: !Ref ListenerIgw
|
||
|
|
|
||
|
|
ListenerRouteTable:
|
||
|
|
Type: AWS::EC2::RouteTable
|
||
|
|
Properties:
|
||
|
|
VpcId: !Ref ListenerVpc
|
||
|
|
|
||
|
|
ListenerRoute:
|
||
|
|
Type: AWS::EC2::Route
|
||
|
|
DependsOn: ListenerIgwAttachment
|
||
|
|
Properties:
|
||
|
|
RouteTableId: !Ref ListenerRouteTable
|
||
|
|
DestinationCidrBlock: 0.0.0.0/0
|
||
|
|
GatewayId: !Ref ListenerIgw
|
||
|
|
|
||
|
|
ListenerSubnetRouteTableAssoc:
|
||
|
|
Type: AWS::EC2::SubnetRouteTableAssociation
|
||
|
|
Properties:
|
||
|
|
SubnetId: !Ref ListenerSubnet
|
||
|
|
RouteTableId: !Ref ListenerRouteTable
|
||
|
|
|
||
|
|
ListenerSecurityGroup:
|
||
|
|
Type: AWS::EC2::SecurityGroup
|
||
|
|
Properties:
|
||
|
|
GroupDescription: exec-aide listener — outbound only
|
||
|
|
VpcId: !Ref ListenerVpc
|
||
|
|
SecurityGroupEgress:
|
||
|
|
- IpProtocol: "-1"
|
||
|
|
CidrIp: 0.0.0.0/0
|
||
|
|
|
||
|
|
ListenerExecutionRole:
|
||
|
|
Type: AWS::IAM::Role
|
||
|
|
Properties:
|
||
|
|
RoleName: exec-aide-listener-execution
|
||
|
|
AssumeRolePolicyDocument:
|
||
|
|
Version: "2012-10-17"
|
||
|
|
Statement:
|
||
|
|
- Effect: Allow
|
||
|
|
Principal:
|
||
|
|
Service: ecs-tasks.amazonaws.com
|
||
|
|
Action: sts:AssumeRole
|
||
|
|
ManagedPolicyArns:
|
||
|
|
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
|
||
|
|
|
||
|
|
ListenerTaskRole:
|
||
|
|
Type: AWS::IAM::Role
|
||
|
|
Properties:
|
||
|
|
RoleName: exec-aide-listener-task
|
||
|
|
AssumeRolePolicyDocument:
|
||
|
|
Version: "2012-10-17"
|
||
|
|
Statement:
|
||
|
|
- Effect: Allow
|
||
|
|
Principal:
|
||
|
|
Service: ecs-tasks.amazonaws.com
|
||
|
|
Action: sts:AssumeRole
|
||
|
|
Policies:
|
||
|
|
- PolicyName: ExecAideListenerAccess
|
||
|
|
PolicyDocument:
|
||
|
|
Version: "2012-10-17"
|
||
|
|
Statement:
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- secretsmanager:GetSecretValue
|
||
|
|
Resource:
|
||
|
|
- !Sub arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:exec-aide/slack-credentials-*
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- ssm:GetParametersByPath
|
||
|
|
- ssm:GetParameter
|
||
|
|
Resource:
|
||
|
|
- !Sub arn:aws:ssm:${AWS::Region}:${AWS::AccountId}:parameter/exec-aide/*
|
||
|
|
- Effect: Allow
|
||
|
|
Action:
|
||
|
|
- dynamodb:GetItem
|
||
|
|
- dynamodb:PutItem
|
||
|
|
- dynamodb:UpdateItem
|
||
|
|
- dynamodb:Query
|
||
|
|
Resource:
|
||
|
|
- !GetAtt ExecAideTable.Arn
|
||
|
|
- !Sub ${ExecAideTable.Arn}/index/*
|
||
|
|
|
||
|
|
ListenerEcrRepo:
|
||
|
|
Type: AWS::ECR::Repository
|
||
|
|
Properties:
|
||
|
|
RepositoryName: exec-aide-listener
|
||
|
|
ImageScanningConfiguration:
|
||
|
|
ScanOnPush: true
|
||
|
|
LifecyclePolicy:
|
||
|
|
LifecyclePolicyText: |
|
||
|
|
{
|
||
|
|
"rules": [
|
||
|
|
{
|
||
|
|
"rulePriority": 1,
|
||
|
|
"description": "Keep last 5 images",
|
||
|
|
"selection": {
|
||
|
|
"tagStatus": "any",
|
||
|
|
"countType": "imageCountMoreThan",
|
||
|
|
"countNumber": 5
|
||
|
|
},
|
||
|
|
"action": { "type": "expire" }
|
||
|
|
}
|
||
|
|
]
|
||
|
|
}
|
||
|
|
|
||
|
|
# ── EventBridge Scheduler (daily digest, DST-aware) ──────
|
||
|
|
|
||
|
|
DailyDigestSchedule:
|
||
|
|
Type: AWS::Scheduler::Schedule
|
||
|
|
Properties:
|
||
|
|
Name: exec-aide-daily-digest
|
||
|
|
Description: Daily 5 PM ET inbox digest
|
||
|
|
ScheduleExpression: cron(0 17 ? * MON-FRI *)
|
||
|
|
ScheduleExpressionTimezone: America/New_York
|
||
|
|
FlexibleTimeWindow:
|
||
|
|
Mode: "OFF"
|
||
|
|
State: ENABLED
|
||
|
|
Target:
|
||
|
|
Arn: !GetAtt DailyDigestFunction.Arn
|
||
|
|
RoleArn: !GetAtt DailyDigestSchedulerRole.Arn
|
||
|
|
|
||
|
|
DailyDigestSchedulerRole:
|
||
|
|
Type: AWS::IAM::Role
|
||
|
|
Properties:
|
||
|
|
RoleName: exec-aide-digest-scheduler
|
||
|
|
AssumeRolePolicyDocument:
|
||
|
|
Version: "2012-10-17"
|
||
|
|
Statement:
|
||
|
|
- Effect: Allow
|
||
|
|
Principal:
|
||
|
|
Service: scheduler.amazonaws.com
|
||
|
|
Action: sts:AssumeRole
|
||
|
|
Policies:
|
||
|
|
- PolicyName: InvokeLambda
|
||
|
|
PolicyDocument:
|
||
|
|
Version: "2012-10-17"
|
||
|
|
Statement:
|
||
|
|
- Effect: Allow
|
||
|
|
Action: lambda:InvokeFunction
|
||
|
|
Resource: !GetAtt DailyDigestFunction.Arn
|
||
|
|
|
||
|
|
DailyDigestSchedulePermission:
|
||
|
|
Type: AWS::Lambda::Permission
|
||
|
|
Properties:
|
||
|
|
FunctionName: !Ref DailyDigestFunction
|
||
|
|
Action: lambda:InvokeFunction
|
||
|
|
Principal: scheduler.amazonaws.com
|
||
|
|
SourceArn: !GetAtt DailyDigestSchedule.Arn
|
||
|
|
|
||
|
|
Outputs:
|
||
|
|
FetchClassifyFunctionArn:
|
||
|
|
Description: Fetch & classify Lambda ARN
|
||
|
|
Value: !GetAtt FetchClassifyFunction.Arn
|
||
|
|
DailyDigestFunctionArn:
|
||
|
|
Description: Daily digest Lambda ARN
|
||
|
|
Value: !GetAtt DailyDigestFunction.Arn
|
||
|
|
ExecAideTableName:
|
||
|
|
Description: DynamoDB table name
|
||
|
|
Value: !Ref ExecAideTable
|
||
|
|
ListenerEcrRepoUri:
|
||
|
|
Description: ECR repo for the Socket Mode listener
|
||
|
|
Value: !GetAtt ListenerEcrRepo.RepositoryUri
|
||
|
|
EcsClusterName:
|
||
|
|
Description: ECS cluster name
|
||
|
|
Value: !Ref EcsCluster
|