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/lib/constructs/socket-mode.ts

133 lines
4.3 KiB
TypeScript
Raw Normal View History

import { Construct } from 'constructs';
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as ecr from 'aws-cdk-lib/aws-ecr';
import * as logs from 'aws-cdk-lib/aws-logs';
import * as iam from 'aws-cdk-lib/aws-iam';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import * as path from 'path';
export interface SocketModeProps {
table: dynamodb.ITable;
conversationFnArn: string;
}
export class SocketModeConstruct extends Construct {
constructor(scope: Construct, id: string, props: SocketModeProps) {
super(scope, id);
const account = cdk.Stack.of(this).account;
const region = cdk.Stack.of(this).region;
// ── VPC ────────────────────────────────────────────────────
const vpc = new ec2.Vpc(this, 'Vpc', {
vpcName: 'exec-aide',
ipAddresses: ec2.IpAddresses.cidr('10.30.0.0/16'),
maxAzs: 1,
natGateways: 0,
subnetConfiguration: [
{
cidrMask: 24,
name: 'exec-aide-listener',
subnetType: ec2.SubnetType.PUBLIC,
},
],
});
const sg = new ec2.SecurityGroup(this, 'ListenerSG', {
vpc,
description: 'exec-aide listener - outbound only',
allowAllOutbound: true,
});
// ── ECR ────────────────────────────────────────────────────
new ecr.Repository(this, 'ListenerRepo', {
repositoryName: 'exec-aide-listener',
imageScanOnPush: true,
lifecycleRules: [
{
maxImageCount: 5,
description: 'Keep last 5 images',
},
],
removalPolicy: cdk.RemovalPolicy.RETAIN,
});
// ── ECS Cluster + Task Definition ──────────────────────────
const cluster = new ecs.Cluster(this, 'Cluster', {
clusterName: 'exec-aide',
vpc,
});
const taskDef = new ecs.FargateTaskDefinition(this, 'TaskDef', {
family: 'exec-aide-listener',
cpu: 256,
memoryLimitMiB: 512,
runtimePlatform: {
cpuArchitecture: ecs.CpuArchitecture.ARM64,
operatingSystemFamily: ecs.OperatingSystemFamily.LINUX,
},
});
taskDef.addContainer('listener', {
image: ecs.ContainerImage.fromAsset(
path.join(__dirname, '../../listener'),
),
essential: true,
environment: {
TABLE_NAME: props.table.tableName,
SECRET_SLACK: 'exec-aide/slack-credentials',
SSM_PREFIX: '/exec-aide',
CONVERSATION_FN_ARN: props.conversationFnArn,
},
logging: ecs.LogDrivers.awsLogs({
streamPrefix: 'listener',
logGroup: new logs.LogGroup(this, 'ListenerLogGroup', {
logGroupName: '/ecs/exec-aide-listener',
retention: logs.RetentionDays.TWO_MONTHS,
}),
}),
});
// ── Task role IAM ──────────────────────────────────────────
props.table.grantReadWriteData(taskDef.taskRole);
taskDef.taskRole.addToPrincipalPolicy(new iam.PolicyStatement({
actions: ['secretsmanager:GetSecretValue'],
resources: [
`arn:aws:secretsmanager:${region}:${account}:secret:exec-aide/slack-credentials-*`,
],
}));
taskDef.taskRole.addToPrincipalPolicy(new iam.PolicyStatement({
actions: ['ssm:GetParametersByPath', 'ssm:GetParameter'],
resources: [
`arn:aws:ssm:${region}:${account}:parameter/exec-aide`,
`arn:aws:ssm:${region}:${account}:parameter/exec-aide/*`,
],
}));
taskDef.taskRole.addToPrincipalPolicy(new iam.PolicyStatement({
actions: ['lambda:InvokeFunction'],
resources: [props.conversationFnArn],
}));
// ── Fargate Service ────────────────────────────────────────
new ecs.FargateService(this, 'Service', {
serviceName: 'exec-aide-listener',
cluster,
taskDefinition: taskDef,
desiredCount: 1,
assignPublicIp: true,
securityGroups: [sg],
vpcSubnets: { subnetType: ec2.SubnetType.PUBLIC },
});
}
}