This repository has been archived on 2026-08-04. You can view files and clone it, but cannot push or open issues or pull requests.
exec-aide/template.yaml

398 lines
12 KiB
YAML
Raw Normal View History

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