@mbc-cqrs-serverless/cli 0.1.22-beta.0 → 0.1.24-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -4
- package/dist/actions/new.action.js +3 -0
- package/package.json +3 -3
- package/templates/.dockerignore +11 -0
- package/templates/Dockerfile +32 -0
- package/templates/README.md +3 -2
- package/templates/infra/.prettierrc +8 -0
- package/templates/infra/README.md +25 -0
- package/templates/infra/asset/schema.graphql +32 -0
- package/templates/infra/bin/infra.ts +47 -0
- package/templates/infra/cdk.json +48 -0
- package/templates/infra/config/constant.ts +8 -0
- package/templates/infra/config/dev/index.ts +48 -0
- package/templates/infra/config/index.ts +17 -0
- package/templates/infra/config/prod/index.ts +48 -0
- package/templates/infra/config/stg/index.ts +48 -0
- package/templates/infra/config/type.ts +53 -0
- package/templates/infra/gitignore +8 -0
- package/templates/infra/jest.config.js +8 -0
- package/templates/infra/libs/build-app.ts +85 -0
- package/templates/infra/libs/infra-stack.ts +948 -0
- package/templates/infra/libs/pipeline-infra-stage.ts +34 -0
- package/templates/infra/libs/pipeline-stack.ts +115 -0
- package/templates/infra/package.json +33 -0
- package/templates/infra/test/.gitkeep +0 -0
- package/templates/infra/tsconfig.json +23 -0
- package/templates/jest.config.json +19 -0
- package/templates/package.json +20 -30
- package/templates/jest.config.js +0 -4
|
@@ -0,0 +1,948 @@
|
|
|
1
|
+
import * as cdk from 'aws-cdk-lib'
|
|
2
|
+
import * as apigwv2 from 'aws-cdk-lib/aws-apigatewayv2'
|
|
3
|
+
import * as apigatewayv2_authorizers from 'aws-cdk-lib/aws-apigatewayv2-authorizers'
|
|
4
|
+
import * as apigwv2_integrations from 'aws-cdk-lib/aws-apigatewayv2-integrations'
|
|
5
|
+
import { Construct } from 'constructs'
|
|
6
|
+
import { randomBytes } from 'crypto'
|
|
7
|
+
|
|
8
|
+
import { IgnoreMode } from 'aws-cdk-lib'
|
|
9
|
+
import { Repository } from 'aws-cdk-lib/aws-ecr'
|
|
10
|
+
import { DockerImageAsset, Platform } from 'aws-cdk-lib/aws-ecr-assets'
|
|
11
|
+
import { ContainerImage } from 'aws-cdk-lib/aws-ecs'
|
|
12
|
+
import { ApplicationLoadBalancedFargateService } from 'aws-cdk-lib/aws-ecs-patterns'
|
|
13
|
+
import { DockerImageName, ECRDeployment } from 'cdk-ecr-deployment'
|
|
14
|
+
import * as path from 'path'
|
|
15
|
+
import {
|
|
16
|
+
ACM_APPSYNC_CERTIFICATE_ARN,
|
|
17
|
+
ACM_HTTP_CERTIFICATE_ARN,
|
|
18
|
+
HOSTED_ZONE_ID,
|
|
19
|
+
HOSTED_ZONE_NAME,
|
|
20
|
+
} from '../config'
|
|
21
|
+
import { Config } from '../config/type'
|
|
22
|
+
import { buildApp } from './build-app'
|
|
23
|
+
|
|
24
|
+
export interface InfraStackProps extends cdk.StackProps {
|
|
25
|
+
config: Config
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class InfraStack extends cdk.Stack {
|
|
29
|
+
public readonly userPoolId: cdk.CfnOutput
|
|
30
|
+
public readonly userPoolClientId: cdk.CfnOutput
|
|
31
|
+
public readonly graphqlApiUrl: cdk.CfnOutput
|
|
32
|
+
public readonly graphqlApiKey: cdk.CfnOutput
|
|
33
|
+
public readonly httpApiUrl: cdk.CfnOutput
|
|
34
|
+
public readonly stateMachineArn: cdk.CfnOutput
|
|
35
|
+
public readonly httpDistributionDomain: cdk.CfnOutput
|
|
36
|
+
|
|
37
|
+
constructor(scope: Construct, id: string, props: InfraStackProps) {
|
|
38
|
+
super(scope, id, props)
|
|
39
|
+
|
|
40
|
+
const name = props.config.appName
|
|
41
|
+
const env = props.config.env
|
|
42
|
+
const prefix = `${env}-${name}-`
|
|
43
|
+
const originVerifyToken = prefix + randomBytes(32).toString('hex')
|
|
44
|
+
|
|
45
|
+
cdk.Tags.of(scope).add('name', props.config.appName)
|
|
46
|
+
cdk.Tags.of(scope).add('env', props.config.env)
|
|
47
|
+
|
|
48
|
+
// Cognito
|
|
49
|
+
let userPool: cdk.aws_cognito.IUserPool
|
|
50
|
+
if (props.config.userPoolId) {
|
|
51
|
+
userPool = cdk.aws_cognito.UserPool.fromUserPoolId(
|
|
52
|
+
this,
|
|
53
|
+
'main-user-pool',
|
|
54
|
+
props.config.userPoolId,
|
|
55
|
+
)
|
|
56
|
+
} else {
|
|
57
|
+
// create new cognito
|
|
58
|
+
userPool = new cdk.aws_cognito.UserPool(this, prefix + 'user-pool', {
|
|
59
|
+
userPoolName: prefix + 'user-pool',
|
|
60
|
+
selfSignUpEnabled: false,
|
|
61
|
+
signInAliases: {
|
|
62
|
+
username: true,
|
|
63
|
+
preferredUsername: true,
|
|
64
|
+
},
|
|
65
|
+
passwordPolicy: {
|
|
66
|
+
minLength: 6,
|
|
67
|
+
requireDigits: false,
|
|
68
|
+
requireLowercase: false,
|
|
69
|
+
requireSymbols: false,
|
|
70
|
+
requireUppercase: false,
|
|
71
|
+
},
|
|
72
|
+
mfa: cdk.aws_cognito.Mfa.OFF,
|
|
73
|
+
accountRecovery: cdk.aws_cognito.AccountRecovery.NONE,
|
|
74
|
+
customAttributes: {
|
|
75
|
+
cci_code: new cdk.aws_cognito.StringAttribute({
|
|
76
|
+
mutable: true,
|
|
77
|
+
maxLen: 20,
|
|
78
|
+
}),
|
|
79
|
+
company_code: new cdk.aws_cognito.StringAttribute({
|
|
80
|
+
mutable: true,
|
|
81
|
+
maxLen: 50,
|
|
82
|
+
}),
|
|
83
|
+
member_id: new cdk.aws_cognito.StringAttribute({
|
|
84
|
+
mutable: true,
|
|
85
|
+
maxLen: 2024,
|
|
86
|
+
}),
|
|
87
|
+
user_type: new cdk.aws_cognito.StringAttribute({
|
|
88
|
+
mutable: true,
|
|
89
|
+
maxLen: 20,
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
email: cdk.aws_cognito.UserPoolEmail.withCognito(),
|
|
93
|
+
deletionProtection: true,
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
this.userPoolId = new cdk.CfnOutput(this, 'UserPoolId', {
|
|
97
|
+
value: userPool.userPoolId,
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
// SNS
|
|
101
|
+
const mainSns = new cdk.aws_sns.Topic(this, 'main-sns', {
|
|
102
|
+
topicName: prefix + 'main-sns',
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
const alarmSns = new cdk.aws_sns.Topic(this, 'alarm-sns', {
|
|
106
|
+
topicName: prefix + 'alarm-sns',
|
|
107
|
+
})
|
|
108
|
+
// SQS
|
|
109
|
+
const taskDlSqs = new cdk.aws_sqs.Queue(this, 'task-dead-letter-sqs', {
|
|
110
|
+
queueName: prefix + 'task-dead-letter-queue',
|
|
111
|
+
})
|
|
112
|
+
const taskSqs = new cdk.aws_sqs.Queue(this, 'task-sqs', {
|
|
113
|
+
queueName: prefix + 'task-action-queue',
|
|
114
|
+
deadLetterQueue: {
|
|
115
|
+
queue: taskDlSqs,
|
|
116
|
+
maxReceiveCount: 5,
|
|
117
|
+
},
|
|
118
|
+
})
|
|
119
|
+
|
|
120
|
+
alarmSns.addSubscription(
|
|
121
|
+
new cdk.aws_sns_subscriptions.SqsSubscription(taskDlSqs, {
|
|
122
|
+
rawMessageDelivery: true,
|
|
123
|
+
}),
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
mainSns.addSubscription(
|
|
127
|
+
new cdk.aws_sns_subscriptions.SqsSubscription(taskSqs, {
|
|
128
|
+
rawMessageDelivery: true,
|
|
129
|
+
filterPolicy: {
|
|
130
|
+
action: cdk.aws_sns.SubscriptionFilter.stringFilter({
|
|
131
|
+
allowlist: ['task-execute'],
|
|
132
|
+
}),
|
|
133
|
+
},
|
|
134
|
+
}),
|
|
135
|
+
)
|
|
136
|
+
const notifySqs = new cdk.aws_sqs.Queue(this, 'notify-sqs', {
|
|
137
|
+
queueName: prefix + 'notification-queue',
|
|
138
|
+
})
|
|
139
|
+
mainSns.addSubscription(
|
|
140
|
+
new cdk.aws_sns_subscriptions.SqsSubscription(notifySqs, {
|
|
141
|
+
rawMessageDelivery: true,
|
|
142
|
+
filterPolicy: {
|
|
143
|
+
action: cdk.aws_sns.SubscriptionFilter.stringFilter({
|
|
144
|
+
allowlist: ['command-status', 'task-status'],
|
|
145
|
+
}),
|
|
146
|
+
},
|
|
147
|
+
}),
|
|
148
|
+
)
|
|
149
|
+
// host zone
|
|
150
|
+
const hostedZone = cdk.aws_route53.HostedZone.fromHostedZoneAttributes(
|
|
151
|
+
this,
|
|
152
|
+
'HostedZone',
|
|
153
|
+
{
|
|
154
|
+
hostedZoneId: HOSTED_ZONE_ID,
|
|
155
|
+
zoneName: HOSTED_ZONE_NAME,
|
|
156
|
+
},
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
// AppSync
|
|
160
|
+
const appSyncCertificate =
|
|
161
|
+
cdk.aws_certificatemanager.Certificate.fromCertificateArn(
|
|
162
|
+
this,
|
|
163
|
+
'appsync-certificate',
|
|
164
|
+
ACM_APPSYNC_CERTIFICATE_ARN,
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
const appSyncApi = new cdk.aws_appsync.GraphqlApi(this, 'realtime', {
|
|
168
|
+
name: prefix + 'realtime',
|
|
169
|
+
definition: cdk.aws_appsync.Definition.fromFile('asset/schema.graphql'), // Define the schema
|
|
170
|
+
authorizationConfig: {
|
|
171
|
+
defaultAuthorization: {
|
|
172
|
+
authorizationType: cdk.aws_appsync.AuthorizationType.API_KEY, // Defining authorization type
|
|
173
|
+
apiKeyConfig: {
|
|
174
|
+
expires: cdk.Expiration.after(cdk.Duration.days(365)), // Set expiration for API Key
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
additionalAuthorizationModes: [
|
|
178
|
+
{
|
|
179
|
+
authorizationType: cdk.aws_appsync.AuthorizationType.IAM,
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
authorizationType: cdk.aws_appsync.AuthorizationType.USER_POOL,
|
|
183
|
+
userPoolConfig: { userPool },
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
xrayEnabled: true, // Enable X-Ray for monitoring
|
|
188
|
+
domainName: {
|
|
189
|
+
certificate: appSyncCertificate,
|
|
190
|
+
domainName: props.config.domain.appsync,
|
|
191
|
+
},
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
const noneDS = appSyncApi.addNoneDataSource('NoneDataSource')
|
|
195
|
+
noneDS.createResolver('sendMessageResolver', {
|
|
196
|
+
typeName: 'Mutation',
|
|
197
|
+
fieldName: 'sendMessage',
|
|
198
|
+
requestMappingTemplate: cdk.aws_appsync.MappingTemplate.fromString(
|
|
199
|
+
'{"version": "2018-05-29","payload": $util.toJson($context.arguments.message)}',
|
|
200
|
+
),
|
|
201
|
+
responseMappingTemplate: cdk.aws_appsync.MappingTemplate.fromString(
|
|
202
|
+
'$util.toJson($context.result)',
|
|
203
|
+
),
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
// route to AppSync
|
|
207
|
+
new cdk.aws_route53.CnameRecord(this, `AppSyncCnameRecord`, {
|
|
208
|
+
zone: hostedZone,
|
|
209
|
+
recordName: props.config.domain.appsync,
|
|
210
|
+
domainName: appSyncApi.appSyncDomainName,
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
this.graphqlApiUrl = new cdk.CfnOutput(this, 'GraphQLAPIURL', {
|
|
214
|
+
value: appSyncApi.graphqlUrl,
|
|
215
|
+
})
|
|
216
|
+
this.graphqlApiKey = new cdk.CfnOutput(this, 'GraphQLAPIKey', {
|
|
217
|
+
value: appSyncApi.apiKey || '',
|
|
218
|
+
})
|
|
219
|
+
// S3
|
|
220
|
+
const ddbBucket = new cdk.aws_s3.Bucket(this, 'ddb-attributes', {
|
|
221
|
+
bucketName: prefix + 'ddb-attributes', // Globally unique bucket name
|
|
222
|
+
versioned: false,
|
|
223
|
+
publicReadAccess: false, // Block public read access
|
|
224
|
+
blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL, // Block all public access
|
|
225
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY, // Define removal policy (use with caution in production)
|
|
226
|
+
cors: [
|
|
227
|
+
{
|
|
228
|
+
allowedHeaders: ['*'],
|
|
229
|
+
allowedMethods: [
|
|
230
|
+
cdk.aws_s3.HttpMethods.GET,
|
|
231
|
+
cdk.aws_s3.HttpMethods.PUT,
|
|
232
|
+
cdk.aws_s3.HttpMethods.POST,
|
|
233
|
+
],
|
|
234
|
+
allowedOrigins: ['*'],
|
|
235
|
+
maxAge: 3000,
|
|
236
|
+
},
|
|
237
|
+
],
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
const publicBucket = new cdk.aws_s3.Bucket(this, 'public-bucket', {
|
|
241
|
+
bucketName: prefix + 'public', // Globally unique bucket name
|
|
242
|
+
versioned: false,
|
|
243
|
+
publicReadAccess: false, // Block public read access
|
|
244
|
+
blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL, // Block all public access
|
|
245
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY, // Define removal policy (use with caution in production)
|
|
246
|
+
cors: [
|
|
247
|
+
{
|
|
248
|
+
allowedHeaders: ['*'],
|
|
249
|
+
allowedMethods: [
|
|
250
|
+
cdk.aws_s3.HttpMethods.GET,
|
|
251
|
+
cdk.aws_s3.HttpMethods.PUT,
|
|
252
|
+
cdk.aws_s3.HttpMethods.POST,
|
|
253
|
+
],
|
|
254
|
+
allowedOrigins: ['*'],
|
|
255
|
+
maxAge: 3000,
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
})
|
|
259
|
+
// cloudfront
|
|
260
|
+
const publicBucketOAI = new cdk.aws_cloudfront.OriginAccessIdentity(
|
|
261
|
+
this,
|
|
262
|
+
'public-bucket-OAI',
|
|
263
|
+
)
|
|
264
|
+
publicBucket.addToResourcePolicy(
|
|
265
|
+
new cdk.aws_iam.PolicyStatement({
|
|
266
|
+
actions: ['s3:GetObject'],
|
|
267
|
+
effect: cdk.aws_iam.Effect.ALLOW,
|
|
268
|
+
resources: [publicBucket.arnForObjects('*')],
|
|
269
|
+
principals: [
|
|
270
|
+
new cdk.aws_iam.CanonicalUserPrincipal(
|
|
271
|
+
publicBucketOAI.cloudFrontOriginAccessIdentityS3CanonicalUserId,
|
|
272
|
+
),
|
|
273
|
+
],
|
|
274
|
+
}),
|
|
275
|
+
)
|
|
276
|
+
const publicBucketDistribution = new cdk.aws_cloudfront.Distribution(
|
|
277
|
+
this,
|
|
278
|
+
'public-bucket-distribution',
|
|
279
|
+
{
|
|
280
|
+
defaultBehavior: {
|
|
281
|
+
allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_GET_HEAD,
|
|
282
|
+
cachedMethods: cdk.aws_cloudfront.CachedMethods.CACHE_GET_HEAD,
|
|
283
|
+
cachePolicy: cdk.aws_cloudfront.CachePolicy.CACHING_OPTIMIZED,
|
|
284
|
+
viewerProtocolPolicy:
|
|
285
|
+
cdk.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
286
|
+
origin: new cdk.aws_cloudfront_origins.S3Origin(publicBucket, {
|
|
287
|
+
originAccessIdentity: publicBucketOAI,
|
|
288
|
+
}),
|
|
289
|
+
},
|
|
290
|
+
priceClass: cdk.aws_cloudfront.PriceClass.PRICE_CLASS_200,
|
|
291
|
+
geoRestriction: cdk.aws_cloudfront.GeoRestriction.allowlist('JP', 'VN'),
|
|
292
|
+
},
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
// VPC
|
|
296
|
+
const vpc = cdk.aws_ec2.Vpc.fromLookup(this, 'main-vpc', {
|
|
297
|
+
vpcId: props.config.vpc.id,
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
const subnets = cdk.aws_ec2.SubnetFilter.byIds(props.config.vpc.subnetIds)
|
|
301
|
+
const securityGroups = props.config.vpc.securityGroupIds.map((id, idx) =>
|
|
302
|
+
cdk.aws_ec2.SecurityGroup.fromSecurityGroupId(
|
|
303
|
+
this,
|
|
304
|
+
'main-security-group-' + idx,
|
|
305
|
+
id,
|
|
306
|
+
),
|
|
307
|
+
)
|
|
308
|
+
// Lambda Layer
|
|
309
|
+
const { layerPath, appPath } = buildApp(env)
|
|
310
|
+
console.log('dist path:', layerPath, appPath)
|
|
311
|
+
const lambdaLayer = new cdk.aws_lambda.LayerVersion(this, 'main-layer', {
|
|
312
|
+
layerVersionName: prefix + 'main-layer',
|
|
313
|
+
code: cdk.aws_lambda.AssetCode.fromAsset(layerPath),
|
|
314
|
+
compatibleRuntimes: [cdk.aws_lambda.Runtime.NODEJS_18_X],
|
|
315
|
+
compatibleArchitectures: [cdk.aws_lambda.Architecture.ARM_64],
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
const commandSfnArn = cdk.Arn.format({
|
|
319
|
+
partition: 'aws',
|
|
320
|
+
region: this.region,
|
|
321
|
+
account: this.account,
|
|
322
|
+
service: 'states',
|
|
323
|
+
resource: 'stateMachine',
|
|
324
|
+
resourceName: prefix + 'command-handler',
|
|
325
|
+
arnFormat: cdk.ArnFormat.COLON_RESOURCE_NAME,
|
|
326
|
+
})
|
|
327
|
+
|
|
328
|
+
// Lambda api ( arm64 )
|
|
329
|
+
const execEnv = {
|
|
330
|
+
NODE_OPTIONS: '--enable-source-maps',
|
|
331
|
+
TZ: 'Asia/Tokyo',
|
|
332
|
+
NODE_ENV: env,
|
|
333
|
+
APP_NAME: name,
|
|
334
|
+
LOG_LEVEL: props.config.logLevel?.level || 'info',
|
|
335
|
+
EVENT_SOURCE_DISABLED: 'false',
|
|
336
|
+
ATTRIBUTE_LIMIT_SIZE: '389120',
|
|
337
|
+
S3_BUCKET_NAME: ddbBucket.bucketName,
|
|
338
|
+
SFN_COMMAND_ARN: commandSfnArn,
|
|
339
|
+
SNS_TOPIC_ARN: mainSns.topicArn,
|
|
340
|
+
COGNITO_USER_POOL_ID: userPool.userPoolId,
|
|
341
|
+
APPSYNC_ENDPOINT: appSyncApi.graphqlUrl,
|
|
342
|
+
SES_FROM_EMAIL: props.config.fromEmailAddress,
|
|
343
|
+
DATABASE_URL: `postgresql://${props.config.rds.accountSsmKey}@${props.config.rds.endpoint}/${props.config.rds.dbName}?schema=public`,
|
|
344
|
+
S3_PUBLIC_BUCKET_NAME: publicBucket.bucketName,
|
|
345
|
+
FRONT_BASE_URL: props.config.frontBaseUrl,
|
|
346
|
+
}
|
|
347
|
+
const lambdaApi = new cdk.aws_lambda.Function(this, 'lambda-api', {
|
|
348
|
+
vpc,
|
|
349
|
+
vpcSubnets: {
|
|
350
|
+
subnetFilters: [subnets],
|
|
351
|
+
},
|
|
352
|
+
securityGroups,
|
|
353
|
+
architecture: cdk.aws_lambda.Architecture.ARM_64,
|
|
354
|
+
functionName: prefix + 'lambda-api',
|
|
355
|
+
layers: [lambdaLayer],
|
|
356
|
+
code: cdk.aws_lambda.Code.fromAsset(appPath),
|
|
357
|
+
handler: 'main.handler',
|
|
358
|
+
runtime: cdk.aws_lambda.Runtime.NODEJS_LATEST,
|
|
359
|
+
timeout: cdk.Duration.seconds(30),
|
|
360
|
+
memorySize: 512,
|
|
361
|
+
tracing: cdk.aws_lambda.Tracing.ACTIVE,
|
|
362
|
+
loggingFormat: cdk.aws_lambda.LoggingFormat.JSON,
|
|
363
|
+
applicationLogLevelV2: props.config.logLevel?.lambdaApplication,
|
|
364
|
+
systemLogLevelV2: props.config.logLevel?.lambdaSystem,
|
|
365
|
+
environment: execEnv,
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// API GW
|
|
369
|
+
const httpApi = new apigwv2.HttpApi(this, 'main-api', {
|
|
370
|
+
description: 'HTTP API for Lambda integration',
|
|
371
|
+
apiName: prefix + 'api',
|
|
372
|
+
corsPreflight: {
|
|
373
|
+
allowOrigins: ['*'],
|
|
374
|
+
allowCredentials: false,
|
|
375
|
+
allowHeaders: ['*'],
|
|
376
|
+
allowMethods: [apigwv2.CorsHttpMethod.ANY],
|
|
377
|
+
maxAge: cdk.Duration.hours(1),
|
|
378
|
+
},
|
|
379
|
+
})
|
|
380
|
+
const lambdaIntegration = new apigwv2_integrations.HttpLambdaIntegration(
|
|
381
|
+
'main-api-lambda',
|
|
382
|
+
lambdaApi,
|
|
383
|
+
)
|
|
384
|
+
// event routes
|
|
385
|
+
httpApi.addRoutes({
|
|
386
|
+
path: '/event/{proxy+}',
|
|
387
|
+
integration: lambdaIntegration,
|
|
388
|
+
authorizer: new apigatewayv2_authorizers.HttpIamAuthorizer(),
|
|
389
|
+
})
|
|
390
|
+
|
|
391
|
+
// api protected routes
|
|
392
|
+
const userPoolClient = new cdk.aws_cognito.UserPoolClient(
|
|
393
|
+
this,
|
|
394
|
+
'apigw-client',
|
|
395
|
+
{
|
|
396
|
+
userPool,
|
|
397
|
+
authFlows: {
|
|
398
|
+
userPassword: true,
|
|
399
|
+
userSrp: true,
|
|
400
|
+
},
|
|
401
|
+
},
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
this.userPoolClientId = new cdk.CfnOutput(this, 'UserPoolClientId', {
|
|
405
|
+
value: userPoolClient.userPoolClientId,
|
|
406
|
+
})
|
|
407
|
+
|
|
408
|
+
const authorizer = new apigatewayv2_authorizers.HttpUserPoolAuthorizer(
|
|
409
|
+
'CognitoAuthorizer',
|
|
410
|
+
userPool,
|
|
411
|
+
{
|
|
412
|
+
userPoolClients: [userPoolClient],
|
|
413
|
+
},
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
let apiIntegration: apigwv2.HttpRouteIntegration
|
|
417
|
+
let taskRole: cdk.aws_iam.Role | undefined
|
|
418
|
+
if (!props.config.ecs) {
|
|
419
|
+
apiIntegration = lambdaIntegration
|
|
420
|
+
} else {
|
|
421
|
+
// ecs api
|
|
422
|
+
const resp = new Repository(this, 'main-ecr-repo', {
|
|
423
|
+
repositoryName: `${prefix}api`,
|
|
424
|
+
removalPolicy: cdk.RemovalPolicy.RETAIN,
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
const image = new DockerImageAsset(this, 'main-image', {
|
|
428
|
+
directory: path.resolve(__dirname, '../..'),
|
|
429
|
+
platform: Platform.LINUX_AMD64,
|
|
430
|
+
ignoreMode: IgnoreMode.DOCKER,
|
|
431
|
+
})
|
|
432
|
+
|
|
433
|
+
const imageTag = process.env.CODEBUILD_RESOLVED_SOURCE_VERSION
|
|
434
|
+
? process.env.CODEBUILD_RESOLVED_SOURCE_VERSION.substring(0, 4)
|
|
435
|
+
: 'latest'
|
|
436
|
+
|
|
437
|
+
new ECRDeployment(this, `${prefix}deploy`, {
|
|
438
|
+
src: new DockerImageName(image.imageUri),
|
|
439
|
+
dest: new DockerImageName(`${resp.repositoryUri}:${imageTag}`),
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
taskRole = new cdk.aws_iam.Role(this, 'ecs-role', {
|
|
443
|
+
assumedBy: new cdk.aws_iam.ServicePrincipal('ecs-tasks.amazonaws.com'),
|
|
444
|
+
})
|
|
445
|
+
|
|
446
|
+
const ecsService = new ApplicationLoadBalancedFargateService(
|
|
447
|
+
this,
|
|
448
|
+
'main-service',
|
|
449
|
+
{
|
|
450
|
+
vpc,
|
|
451
|
+
taskSubnets: {
|
|
452
|
+
subnetFilters: [subnets],
|
|
453
|
+
},
|
|
454
|
+
securityGroups,
|
|
455
|
+
circuitBreaker: {
|
|
456
|
+
rollback: props.config.ecs.autoRollback,
|
|
457
|
+
},
|
|
458
|
+
publicLoadBalancer: false,
|
|
459
|
+
memoryLimitMiB: props.config.ecs.memory,
|
|
460
|
+
cpu: props.config.ecs.cpu,
|
|
461
|
+
desiredCount: props.config.ecs.minInstances,
|
|
462
|
+
taskImageOptions: {
|
|
463
|
+
image: ContainerImage.fromDockerImageAsset(image),
|
|
464
|
+
environment: {
|
|
465
|
+
...execEnv,
|
|
466
|
+
APP_PORT: '80',
|
|
467
|
+
EVENT_SOURCE_DISABLED: 'true',
|
|
468
|
+
PRISMA_EXPLICIT_CONNECT: 'false',
|
|
469
|
+
},
|
|
470
|
+
secrets: {
|
|
471
|
+
DATABASE_USER_PASS: cdk.aws_ecs.Secret.fromSsmParameter(
|
|
472
|
+
cdk.aws_ssm.StringParameter.fromSecureStringParameterAttributes(
|
|
473
|
+
this,
|
|
474
|
+
'dbUserPass',
|
|
475
|
+
{
|
|
476
|
+
parameterName: props.config.rds.accountSsmKey,
|
|
477
|
+
},
|
|
478
|
+
),
|
|
479
|
+
),
|
|
480
|
+
},
|
|
481
|
+
taskRole,
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
if (props.config.ecs.cpuThreshold) {
|
|
487
|
+
const scalableTarget = ecsService.service.autoScaleTaskCount({
|
|
488
|
+
minCapacity: props.config.ecs.minInstances,
|
|
489
|
+
maxCapacity: props.config.ecs.maxInstances,
|
|
490
|
+
})
|
|
491
|
+
|
|
492
|
+
scalableTarget.scaleOnCpuUtilization('CpuScaling', {
|
|
493
|
+
targetUtilizationPercent: props.config.ecs.cpuThreshold,
|
|
494
|
+
})
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const vpcLink = new apigwv2.VpcLink(this, 'ecs-vpc-link', {
|
|
498
|
+
vpc,
|
|
499
|
+
})
|
|
500
|
+
const vpcLinkIntegration = new apigwv2_integrations.HttpAlbIntegration(
|
|
501
|
+
'ecs-vpc-link-integration',
|
|
502
|
+
ecsService.loadBalancer.listeners[0],
|
|
503
|
+
{
|
|
504
|
+
vpcLink,
|
|
505
|
+
parameterMapping: new apigwv2.ParameterMapping()
|
|
506
|
+
.appendHeader(
|
|
507
|
+
'x-source-ip',
|
|
508
|
+
apigwv2.MappingValue.contextVariable('identity.sourceIp'),
|
|
509
|
+
)
|
|
510
|
+
.appendHeader(
|
|
511
|
+
'x-request-id',
|
|
512
|
+
apigwv2.MappingValue.contextVariable('extendedRequestId'),
|
|
513
|
+
),
|
|
514
|
+
},
|
|
515
|
+
)
|
|
516
|
+
apiIntegration = vpcLinkIntegration
|
|
517
|
+
}
|
|
518
|
+
// health check api (public)
|
|
519
|
+
httpApi.addRoutes({
|
|
520
|
+
path: '/',
|
|
521
|
+
methods: [apigwv2.HttpMethod.GET],
|
|
522
|
+
integration: apiIntegration,
|
|
523
|
+
})
|
|
524
|
+
// protected api
|
|
525
|
+
httpApi.addRoutes({
|
|
526
|
+
path: '/{proxy+}',
|
|
527
|
+
methods: [
|
|
528
|
+
apigwv2.HttpMethod.HEAD,
|
|
529
|
+
apigwv2.HttpMethod.GET,
|
|
530
|
+
apigwv2.HttpMethod.POST,
|
|
531
|
+
apigwv2.HttpMethod.DELETE,
|
|
532
|
+
apigwv2.HttpMethod.PUT,
|
|
533
|
+
apigwv2.HttpMethod.PATCH,
|
|
534
|
+
],
|
|
535
|
+
integration: apiIntegration,
|
|
536
|
+
authorizer,
|
|
537
|
+
})
|
|
538
|
+
// Output the URL of the HTTP API
|
|
539
|
+
this.httpApiUrl = new cdk.CfnOutput(this, 'HttpApiUrl', {
|
|
540
|
+
value: httpApi.url!,
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
// cloudfront to HTTP API
|
|
544
|
+
const httpDistributionCertificate =
|
|
545
|
+
cdk.aws_certificatemanager.Certificate.fromCertificateArn(
|
|
546
|
+
this,
|
|
547
|
+
'http-distribution-certificate',
|
|
548
|
+
ACM_HTTP_CERTIFICATE_ARN,
|
|
549
|
+
)
|
|
550
|
+
const httpDistribution = new cdk.aws_cloudfront.Distribution(
|
|
551
|
+
this,
|
|
552
|
+
'http-distribution',
|
|
553
|
+
{
|
|
554
|
+
defaultBehavior: {
|
|
555
|
+
origin: new cdk.aws_cloudfront_origins.HttpOrigin(
|
|
556
|
+
`${httpApi.apiId}.execute-api.${this.region}.amazonaws.com`,
|
|
557
|
+
{
|
|
558
|
+
customHeaders: {
|
|
559
|
+
'X-Origin-Verify': originVerifyToken,
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
),
|
|
563
|
+
originRequestPolicy:
|
|
564
|
+
cdk.aws_cloudfront.OriginRequestPolicy
|
|
565
|
+
.ALL_VIEWER_EXCEPT_HOST_HEADER,
|
|
566
|
+
responseHeadersPolicy:
|
|
567
|
+
cdk.aws_cloudfront.ResponseHeadersPolicy.CORS_ALLOW_ALL_ORIGINS,
|
|
568
|
+
allowedMethods: cdk.aws_cloudfront.AllowedMethods.ALLOW_ALL,
|
|
569
|
+
cachePolicy: cdk.aws_cloudfront.CachePolicy.CACHING_DISABLED,
|
|
570
|
+
viewerProtocolPolicy:
|
|
571
|
+
cdk.aws_cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
|
|
572
|
+
},
|
|
573
|
+
priceClass: cdk.aws_cloudfront.PriceClass.PRICE_CLASS_200,
|
|
574
|
+
geoRestriction: cdk.aws_cloudfront.GeoRestriction.allowlist('JP', 'VN'),
|
|
575
|
+
domainNames: [props.config.domain.http],
|
|
576
|
+
certificate: httpDistributionCertificate,
|
|
577
|
+
webAclId: props.config.wafArn,
|
|
578
|
+
enableIpv6: false,
|
|
579
|
+
},
|
|
580
|
+
)
|
|
581
|
+
|
|
582
|
+
new cdk.aws_route53.CnameRecord(this, 'http-distribution-a-record', {
|
|
583
|
+
zone: hostedZone,
|
|
584
|
+
recordName: props.config.domain.http,
|
|
585
|
+
domainName: httpDistribution.distributionDomainName,
|
|
586
|
+
})
|
|
587
|
+
|
|
588
|
+
this.httpDistributionDomain = new cdk.CfnOutput(
|
|
589
|
+
this,
|
|
590
|
+
'http-distribution-domain',
|
|
591
|
+
{
|
|
592
|
+
value: httpDistribution.distributionDomainName,
|
|
593
|
+
},
|
|
594
|
+
)
|
|
595
|
+
|
|
596
|
+
// api gateway logging
|
|
597
|
+
// Setup the access log for APIGWv2
|
|
598
|
+
const httpApiAccessLogs = new cdk.aws_logs.LogGroup(
|
|
599
|
+
this,
|
|
600
|
+
'http-api-AccessLogs',
|
|
601
|
+
)
|
|
602
|
+
const httpApiDefaultStage = httpApi.defaultStage?.node
|
|
603
|
+
.defaultChild as cdk.aws_apigatewayv2.CfnStage
|
|
604
|
+
httpApiDefaultStage.accessLogSettings = {
|
|
605
|
+
destinationArn: httpApiAccessLogs.logGroupArn,
|
|
606
|
+
format: JSON.stringify({
|
|
607
|
+
requestId: '$context.requestId',
|
|
608
|
+
ip: '$context.identity.sourceIp',
|
|
609
|
+
userAgent: '$context.identity.userAgent',
|
|
610
|
+
sourceIp: '$context.identity.sourceIp',
|
|
611
|
+
requestTime: '$context.requestTime',
|
|
612
|
+
requestTimeEpoch: '$context.requestTimeEpoch',
|
|
613
|
+
httpMethod: '$context.httpMethod',
|
|
614
|
+
routeKey: '$context.routeKey',
|
|
615
|
+
path: '$context.path',
|
|
616
|
+
status: '$context.status',
|
|
617
|
+
protocol: '$context.protocol',
|
|
618
|
+
responseLength: '$context.responseLength',
|
|
619
|
+
domainName: '$context.domainName',
|
|
620
|
+
responseLatency: '$context.responseLatency',
|
|
621
|
+
integrationLatency: '$context.integrationLatency',
|
|
622
|
+
username: '$context.authorizer.claims.sub',
|
|
623
|
+
}),
|
|
624
|
+
}
|
|
625
|
+
httpApiDefaultStage.defaultRouteSettings = {
|
|
626
|
+
detailedMetricsEnabled: true,
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
// StepFunction
|
|
630
|
+
// Define the lambda invoke task with common configurations
|
|
631
|
+
const lambdaInvoke = (
|
|
632
|
+
stateName: string,
|
|
633
|
+
nextState: cdk.aws_stepfunctions.IChainable | null,
|
|
634
|
+
integrationPattern: cdk.aws_stepfunctions.IntegrationPattern,
|
|
635
|
+
) => {
|
|
636
|
+
const payloadObject: {
|
|
637
|
+
[key: string]: any
|
|
638
|
+
} = {
|
|
639
|
+
'input.$': '$',
|
|
640
|
+
'context.$': '$$',
|
|
641
|
+
}
|
|
642
|
+
if (
|
|
643
|
+
integrationPattern ===
|
|
644
|
+
cdk.aws_stepfunctions.IntegrationPattern.WAIT_FOR_TASK_TOKEN
|
|
645
|
+
) {
|
|
646
|
+
payloadObject['taskToken'] = cdk.aws_stepfunctions.JsonPath.taskToken // '$$.Task.Token'
|
|
647
|
+
}
|
|
648
|
+
const lambdaTask = new cdk.aws_stepfunctions_tasks.LambdaInvoke(
|
|
649
|
+
this,
|
|
650
|
+
stateName,
|
|
651
|
+
{
|
|
652
|
+
lambdaFunction: lambdaApi,
|
|
653
|
+
payload: cdk.aws_stepfunctions.TaskInput.fromObject(payloadObject),
|
|
654
|
+
retryOnServiceExceptions: true,
|
|
655
|
+
stateName,
|
|
656
|
+
outputPath: '$.Payload[0][0]',
|
|
657
|
+
integrationPattern,
|
|
658
|
+
},
|
|
659
|
+
)
|
|
660
|
+
if (nextState) {
|
|
661
|
+
return lambdaTask.next(nextState)
|
|
662
|
+
}
|
|
663
|
+
return lambdaTask
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Define states
|
|
667
|
+
const fail = new cdk.aws_stepfunctions.Fail(this, 'fail', {
|
|
668
|
+
stateName: 'fail',
|
|
669
|
+
causePath: '$.cause',
|
|
670
|
+
errorPath: '$.error',
|
|
671
|
+
})
|
|
672
|
+
const success = new cdk.aws_stepfunctions.Succeed(this, 'success', {
|
|
673
|
+
stateName: 'success',
|
|
674
|
+
})
|
|
675
|
+
const finish = lambdaInvoke(
|
|
676
|
+
'finish',
|
|
677
|
+
success,
|
|
678
|
+
cdk.aws_stepfunctions.IntegrationPattern.REQUEST_RESPONSE,
|
|
679
|
+
)
|
|
680
|
+
const syncData = lambdaInvoke(
|
|
681
|
+
'sync_data',
|
|
682
|
+
null,
|
|
683
|
+
cdk.aws_stepfunctions.IntegrationPattern.REQUEST_RESPONSE,
|
|
684
|
+
)
|
|
685
|
+
// Define Map state
|
|
686
|
+
const syncDataAll = new cdk.aws_stepfunctions.Map(this, 'sync_data_all', {
|
|
687
|
+
stateName: 'sync_data_all',
|
|
688
|
+
maxConcurrency: 0,
|
|
689
|
+
itemsPath: cdk.aws_stepfunctions.JsonPath.stringAt('$'),
|
|
690
|
+
})
|
|
691
|
+
.itemProcessor(syncData)
|
|
692
|
+
.next(finish)
|
|
693
|
+
const transformData = lambdaInvoke(
|
|
694
|
+
'transform_data',
|
|
695
|
+
syncDataAll,
|
|
696
|
+
cdk.aws_stepfunctions.IntegrationPattern.REQUEST_RESPONSE,
|
|
697
|
+
)
|
|
698
|
+
const historyCopy = lambdaInvoke(
|
|
699
|
+
'history_copy',
|
|
700
|
+
transformData,
|
|
701
|
+
cdk.aws_stepfunctions.IntegrationPattern.REQUEST_RESPONSE,
|
|
702
|
+
)
|
|
703
|
+
const waitPrevCommand = lambdaInvoke(
|
|
704
|
+
'wait_prev_command',
|
|
705
|
+
historyCopy,
|
|
706
|
+
cdk.aws_stepfunctions.IntegrationPattern.WAIT_FOR_TASK_TOKEN,
|
|
707
|
+
)
|
|
708
|
+
|
|
709
|
+
// Define Choice state
|
|
710
|
+
const checkVersionResult = new cdk.aws_stepfunctions.Choice(
|
|
711
|
+
this,
|
|
712
|
+
'check_version_result',
|
|
713
|
+
{
|
|
714
|
+
stateName: 'check_version_result',
|
|
715
|
+
},
|
|
716
|
+
)
|
|
717
|
+
.when(
|
|
718
|
+
cdk.aws_stepfunctions.Condition.numberEquals('$.result', 0),
|
|
719
|
+
historyCopy,
|
|
720
|
+
)
|
|
721
|
+
.when(
|
|
722
|
+
cdk.aws_stepfunctions.Condition.numberEquals('$.result', 1),
|
|
723
|
+
waitPrevCommand,
|
|
724
|
+
)
|
|
725
|
+
.when(cdk.aws_stepfunctions.Condition.numberEquals('$.result', -1), fail)
|
|
726
|
+
.otherwise(waitPrevCommand)
|
|
727
|
+
|
|
728
|
+
const sfnDefinition = lambdaInvoke(
|
|
729
|
+
'check_version',
|
|
730
|
+
checkVersionResult,
|
|
731
|
+
cdk.aws_stepfunctions.IntegrationPattern.REQUEST_RESPONSE,
|
|
732
|
+
)
|
|
733
|
+
|
|
734
|
+
const sfnLogGroup = new cdk.aws_logs.LogGroup(
|
|
735
|
+
this,
|
|
736
|
+
'command-handler-sfn-log',
|
|
737
|
+
{
|
|
738
|
+
logGroupName: `/aws/vendedlogs/states/${prefix}-command-handler-state-machine-Logs`, // Specify a log group name
|
|
739
|
+
removalPolicy: cdk.RemovalPolicy.DESTROY, // Policy for log group removal
|
|
740
|
+
retention: cdk.aws_logs.RetentionDays.SIX_MONTHS,
|
|
741
|
+
},
|
|
742
|
+
)
|
|
743
|
+
|
|
744
|
+
// Define the state machine
|
|
745
|
+
const stateMachine = new cdk.aws_stepfunctions.StateMachine(
|
|
746
|
+
this,
|
|
747
|
+
'command-handler-state-machine',
|
|
748
|
+
{
|
|
749
|
+
stateMachineName: prefix + 'command-handler',
|
|
750
|
+
comment: 'A state machine that run the command stream handler',
|
|
751
|
+
definitionBody:
|
|
752
|
+
cdk.aws_stepfunctions.DefinitionBody.fromChainable(sfnDefinition),
|
|
753
|
+
tracingEnabled: true,
|
|
754
|
+
logs: {
|
|
755
|
+
destination: sfnLogGroup,
|
|
756
|
+
level: cdk.aws_stepfunctions.LogLevel.ALL, // Log level (ALL, ERROR, or FATAL)
|
|
757
|
+
},
|
|
758
|
+
},
|
|
759
|
+
)
|
|
760
|
+
|
|
761
|
+
// Output the State Machine's ARN
|
|
762
|
+
this.stateMachineArn = new cdk.CfnOutput(this, 'StateMachineArn', {
|
|
763
|
+
value: stateMachine.stateMachineArn,
|
|
764
|
+
})
|
|
765
|
+
|
|
766
|
+
// add event sources to lambda event
|
|
767
|
+
lambdaApi.addEventSource(
|
|
768
|
+
new cdk.aws_lambda_event_sources.SqsEventSource(taskSqs, {
|
|
769
|
+
batchSize: 1,
|
|
770
|
+
}),
|
|
771
|
+
)
|
|
772
|
+
lambdaApi.addEventSource(
|
|
773
|
+
new cdk.aws_lambda_event_sources.SqsEventSource(notifySqs, {
|
|
774
|
+
batchSize: 1,
|
|
775
|
+
}),
|
|
776
|
+
)
|
|
777
|
+
// dynamodb event source
|
|
778
|
+
const tableNames = ['tasks', 'master-command']
|
|
779
|
+
for (const tableName of tableNames) {
|
|
780
|
+
const tableDesc = new cdk.custom_resources.AwsCustomResource(
|
|
781
|
+
this,
|
|
782
|
+
tableName + '-decs',
|
|
783
|
+
{
|
|
784
|
+
onCreate: {
|
|
785
|
+
service: 'DynamoDB',
|
|
786
|
+
action: 'describeTable',
|
|
787
|
+
parameters: {
|
|
788
|
+
TableName: prefix + tableName,
|
|
789
|
+
},
|
|
790
|
+
physicalResourceId:
|
|
791
|
+
cdk.custom_resources.PhysicalResourceId.fromResponse(
|
|
792
|
+
'Table.TableArn',
|
|
793
|
+
),
|
|
794
|
+
},
|
|
795
|
+
policy: cdk.custom_resources.AwsCustomResourcePolicy.fromSdkCalls({
|
|
796
|
+
resources:
|
|
797
|
+
cdk.custom_resources.AwsCustomResourcePolicy.ANY_RESOURCE,
|
|
798
|
+
}),
|
|
799
|
+
},
|
|
800
|
+
)
|
|
801
|
+
const tableCdk = cdk.aws_dynamodb.Table.fromTableAttributes(
|
|
802
|
+
this,
|
|
803
|
+
tableName + '-table',
|
|
804
|
+
{
|
|
805
|
+
tableArn: tableDesc.getResponseField('Table.TableArn'),
|
|
806
|
+
tableStreamArn: tableDesc.getResponseField('Table.LatestStreamArn'),
|
|
807
|
+
},
|
|
808
|
+
)
|
|
809
|
+
lambdaApi.addEventSource(
|
|
810
|
+
new cdk.aws_lambda_event_sources.DynamoEventSource(tableCdk, {
|
|
811
|
+
startingPosition: cdk.aws_lambda.StartingPosition.TRIM_HORIZON,
|
|
812
|
+
batchSize: 1,
|
|
813
|
+
filters: [
|
|
814
|
+
cdk.aws_lambda.FilterCriteria.filter({
|
|
815
|
+
eventName: cdk.aws_lambda.FilterRule.isEqual('INSERT'),
|
|
816
|
+
}),
|
|
817
|
+
],
|
|
818
|
+
}),
|
|
819
|
+
)
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// add lambda role
|
|
823
|
+
userPool.grant(
|
|
824
|
+
lambdaApi,
|
|
825
|
+
'cognito-idp:AdminGetUser',
|
|
826
|
+
'cognito-idp:AdminAddUserToGroup',
|
|
827
|
+
'cognito-idp:AdminCreateUser',
|
|
828
|
+
'cognito-idp:AdminDeleteUser',
|
|
829
|
+
'cognito-idp:AdminDisableUser',
|
|
830
|
+
'cognito-idp:AdminEnableUser',
|
|
831
|
+
'cognito-idp:AdminSetUserPassword',
|
|
832
|
+
'cognito-idp:AdminResetUserPassword',
|
|
833
|
+
'cognito-idp:AdminUpdateUserAttributes',
|
|
834
|
+
)
|
|
835
|
+
ddbBucket.grantReadWrite(lambdaApi)
|
|
836
|
+
publicBucket.grantReadWrite(lambdaApi)
|
|
837
|
+
mainSns.grantPublish(lambdaApi)
|
|
838
|
+
taskSqs.grantSendMessages(lambdaApi)
|
|
839
|
+
notifySqs.grantSendMessages(lambdaApi)
|
|
840
|
+
appSyncApi.grantMutation(lambdaApi)
|
|
841
|
+
|
|
842
|
+
// Define an IAM policy for full DynamoDB access
|
|
843
|
+
const dynamoDbTablePrefixArn = cdk.Arn.format({
|
|
844
|
+
partition: 'aws',
|
|
845
|
+
region: this.region,
|
|
846
|
+
account: this.account,
|
|
847
|
+
service: 'dynamodb',
|
|
848
|
+
resource: 'table',
|
|
849
|
+
resourceName: prefix + '*',
|
|
850
|
+
})
|
|
851
|
+
const dynamodbPolicy = new cdk.aws_iam.PolicyStatement({
|
|
852
|
+
actions: [
|
|
853
|
+
'dynamodb:PutItem',
|
|
854
|
+
'dynamodb:UpdateItem',
|
|
855
|
+
'dynamodb:GetItem',
|
|
856
|
+
'dynamodb:Query',
|
|
857
|
+
],
|
|
858
|
+
resources: [dynamoDbTablePrefixArn], // Access to all resources
|
|
859
|
+
})
|
|
860
|
+
|
|
861
|
+
// Attach the policy to the Lambda function's execution role
|
|
862
|
+
lambdaApi.role?.attachInlinePolicy(
|
|
863
|
+
new cdk.aws_iam.Policy(this, 'lambda-api-ddb-policy', {
|
|
864
|
+
statements: [dynamodbPolicy],
|
|
865
|
+
}),
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
const sfnPolicy = new cdk.aws_iam.PolicyStatement({
|
|
869
|
+
actions: [
|
|
870
|
+
'states:StartExecution',
|
|
871
|
+
'states:GetExecutionHistory',
|
|
872
|
+
'states:DescribeExecution',
|
|
873
|
+
],
|
|
874
|
+
resources: [commandSfnArn],
|
|
875
|
+
})
|
|
876
|
+
|
|
877
|
+
// Attach the policy to the Lambda function's execution role
|
|
878
|
+
lambdaApi.role?.attachInlinePolicy(
|
|
879
|
+
new cdk.aws_iam.Policy(this, 'lambda-event-sfn-policy', {
|
|
880
|
+
statements: [sfnPolicy],
|
|
881
|
+
}),
|
|
882
|
+
)
|
|
883
|
+
|
|
884
|
+
const sesPolicy = new cdk.aws_iam.PolicyStatement({
|
|
885
|
+
actions: ['ses:SendEmail'],
|
|
886
|
+
resources: ['*'],
|
|
887
|
+
})
|
|
888
|
+
|
|
889
|
+
// Attach the policy to the Lambda function's execution role
|
|
890
|
+
lambdaApi.role?.attachInlinePolicy(
|
|
891
|
+
new cdk.aws_iam.Policy(this, 'lambda-ses-policy', {
|
|
892
|
+
statements: [sesPolicy],
|
|
893
|
+
}),
|
|
894
|
+
)
|
|
895
|
+
|
|
896
|
+
const ssmPolicy = new cdk.aws_iam.PolicyStatement({
|
|
897
|
+
actions: ['ssm:GetParameter', 'kms:Decrypt'],
|
|
898
|
+
resources: ['*'],
|
|
899
|
+
})
|
|
900
|
+
|
|
901
|
+
// allow lambdaApi role to access ssm
|
|
902
|
+
lambdaApi.role?.attachInlinePolicy(
|
|
903
|
+
new cdk.aws_iam.Policy(this, 'lambda-api-ssm-policy', {
|
|
904
|
+
statements: [ssmPolicy],
|
|
905
|
+
}),
|
|
906
|
+
)
|
|
907
|
+
|
|
908
|
+
if (!!taskRole) {
|
|
909
|
+
ddbBucket.grantReadWrite(taskRole)
|
|
910
|
+
publicBucket.grantReadWrite(taskRole)
|
|
911
|
+
mainSns.grantPublish(taskRole)
|
|
912
|
+
taskSqs.grantSendMessages(taskRole)
|
|
913
|
+
notifySqs.grantSendMessages(taskRole)
|
|
914
|
+
appSyncApi.grantMutation(taskRole)
|
|
915
|
+
taskRole.addToPrincipalPolicy(
|
|
916
|
+
new cdk.aws_iam.PolicyStatement({
|
|
917
|
+
actions: [
|
|
918
|
+
'ssmmessages:CreateControlChannel',
|
|
919
|
+
'ssmmessages:CreateDataChannel',
|
|
920
|
+
'ssmmessages:OpenControlChannel',
|
|
921
|
+
'ssmmessages:OpenDataChannel',
|
|
922
|
+
],
|
|
923
|
+
resources: ['*'],
|
|
924
|
+
}),
|
|
925
|
+
)
|
|
926
|
+
taskRole.attachInlinePolicy(
|
|
927
|
+
new cdk.aws_iam.Policy(this, 'ecs-api-ddb-policy', {
|
|
928
|
+
statements: [dynamodbPolicy],
|
|
929
|
+
}),
|
|
930
|
+
)
|
|
931
|
+
taskRole.attachInlinePolicy(
|
|
932
|
+
new cdk.aws_iam.Policy(this, 'ecs-event-sfn-policy', {
|
|
933
|
+
statements: [sfnPolicy],
|
|
934
|
+
}),
|
|
935
|
+
)
|
|
936
|
+
taskRole.attachInlinePolicy(
|
|
937
|
+
new cdk.aws_iam.Policy(this, 'ecs-ses-policy', {
|
|
938
|
+
statements: [sesPolicy],
|
|
939
|
+
}),
|
|
940
|
+
)
|
|
941
|
+
taskRole.attachInlinePolicy(
|
|
942
|
+
new cdk.aws_iam.Policy(this, 'ecs-api-ssm-policy', {
|
|
943
|
+
statements: [ssmPolicy],
|
|
944
|
+
}),
|
|
945
|
+
)
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
}
|