@@ -46,7 +46,7 @@ import { QueueEncryption } from "@aws-cdk/aws-sqs";
46
46
import { LogGroup } from "@aws-cdk/aws-logs" ;
47
47
import { LogGroupLogDestination } from "@aws-cdk/aws-apigateway" ;
48
48
49
- const API_CONCURRENT_REQUESTS = 20 ; //approximate number of 1-2 page documents to be processed parallelly
49
+ const API_CONCURRENT_REQUESTS = 20 ; //approximate number of 1-2 page documents to be processed in parallell
50
50
51
51
export interface TextractStackProps {
52
52
email : string ;
@@ -171,12 +171,14 @@ export class CdkTextractStack extends cdk.Stack {
171
171
behaviors : [ { isDefaultBehavior : true } ] ,
172
172
} ,
173
173
] ,
174
- errorConfigurations : [ {
175
- errorCode : 404 ,
176
- responseCode : 200 ,
177
- errorCachingMinTtl : 5 ,
178
- responsePagePath : '/index.html'
179
- } ] ,
174
+ errorConfigurations : [
175
+ {
176
+ errorCode : 404 ,
177
+ responseCode : 200 ,
178
+ errorCachingMinTtl : 5 ,
179
+ responsePagePath : "/index.html" ,
180
+ } ,
181
+ ] ,
180
182
priceClass : PriceClass . PRICE_CLASS_100 ,
181
183
httpVersion : HttpVersion . HTTP2 ,
182
184
enableIpV6 : true ,
@@ -231,11 +233,19 @@ export class CdkTextractStack extends cdk.Stack {
231
233
cloudfrontDocumentsBucketPolicyStatement
232
234
) ;
233
235
234
- const esLogGroup = new LogGroup (
236
+ const esSearchLogGroup = new LogGroup (
237
+ this ,
238
+ this . resourceName ( "ElasticSearchSearchLogGroup" ) ,
239
+ {
240
+ logGroupName : this . resourceName ( "ElasticSearchSearchLogGroup" ) ,
241
+ }
242
+ ) ;
243
+
244
+ const esIndexLogGroup = new LogGroup (
235
245
this ,
236
- this . resourceName ( "ElasticSearchLogGroup " ) ,
246
+ this . resourceName ( "ElasticSearchIndexLogGroup " ) ,
237
247
{
238
- logGroupName : this . resourceName ( "ElasticSearchLogGroup " ) ,
248
+ logGroupName : this . resourceName ( "ElasticSearchIndexLogGroup " ) ,
239
249
}
240
250
) ;
241
251
@@ -270,18 +280,6 @@ export class CdkTextractStack extends cdk.Stack {
270
280
}
271
281
) ;
272
282
} else {
273
- const serviceLinkedRole = new cdk . CfnResource (
274
- this ,
275
- this . resourceName ( "es-service-linked-role" ) ,
276
- {
277
- type : "AWS::IAM::ServiceLinkedRole" ,
278
- properties : {
279
- AWSServiceName : "es.amazonaws.com" ,
280
- Description : "Role for ES to access resources in my VPC" ,
281
- } ,
282
- }
283
- ) ;
284
-
285
283
elasticSearch = new es . CfnDomain (
286
284
this ,
287
285
this . resourceName ( "ElasticSearchCluster" ) ,
@@ -308,28 +306,43 @@ export class CdkTextractStack extends cdk.Stack {
308
306
nodeToNodeEncryptionOptions : {
309
307
enabled : true ,
310
308
} ,
311
- logPublishingOptions : {
312
- INDEX_SLOW_LOGS : {
313
- cloudWatchLogsLogGroupArn : esLogGroup . logGroupArn ,
314
- enabled : true ,
315
- } ,
316
- SEARCH_SLOW_LOGS : {
317
- cloudWatchLogsLogGroupArn : esLogGroup . logGroupArn ,
318
- enabled : true ,
319
- } ,
320
- } ,
321
309
}
322
310
) ;
323
-
324
- elasticSearch . node . addDependency ( serviceLinkedRole ) ;
325
311
}
326
312
313
+ const jobResultsKey = new kms . Key (
314
+ this ,
315
+ this . resourceName ( "JobResultsKey" ) ,
316
+ {
317
+ enableKeyRotation : true ,
318
+ enabled : true ,
319
+ trustAccountIdentities : true ,
320
+ policy : new iam . PolicyDocument ( {
321
+ assignSids : true ,
322
+ statements : [
323
+ new iam . PolicyStatement ( {
324
+ actions : [ "kms:GenerateDataKey*" , "kms:Decrypt" ] ,
325
+ resources : [ "*" ] , // Resource level permissions are not necessary in this policy statement, as it is automatically restricted to this key
326
+ effect : iam . Effect . ALLOW ,
327
+ principals : [
328
+ new iam . ServicePrincipal ( "sns.amazonaws.com" ) ,
329
+ new iam . ServicePrincipal ( "lambda.amazonaws.com" ) ,
330
+ new iam . ServicePrincipal ( "textract.amazonaws.com" ) ,
331
+ new iam . ServicePrincipal ( "sqs.amazonaws.com" ) ,
332
+ ] ,
333
+ } ) ,
334
+ ] ,
335
+ } ) ,
336
+ }
337
+ ) ;
338
+
327
339
// SNS Topic
328
340
const jobCompletionTopic = new sns . Topic (
329
341
this ,
330
- this . resourceName ( "JobCompletion " ) ,
342
+ this . resourceName ( "JobCompletionTopic " ) ,
331
343
{
332
344
displayName : "Job completion topic" ,
345
+ masterKey : jobResultsKey ,
333
346
}
334
347
) ;
335
348
@@ -349,6 +362,13 @@ export class CdkTextractStack extends cdk.Stack {
349
362
resources : [ jobCompletionTopic . topicArn ] ,
350
363
} )
351
364
) ;
365
+ textractServiceRole . addToPolicy (
366
+ new iam . PolicyStatement ( {
367
+ effect : iam . Effect . ALLOW ,
368
+ actions : [ "kms:Decrypt" , "kms:GenerateDataKey*" ] ,
369
+ resources : [ jobResultsKey . keyArn ] ,
370
+ } )
371
+ ) ;
352
372
353
373
// DynamoDB tables
354
374
const outputTable = new ddb . Table ( this , this . resourceName ( "OutputTable" ) , {
@@ -440,6 +460,7 @@ export class CdkTextractStack extends cdk.Stack {
440
460
{
441
461
visibilityTimeout : cdk . Duration . seconds ( 900 ) ,
442
462
retentionPeriod : cdk . Duration . seconds ( 1209600 ) ,
463
+ encryption : QueueEncryption . KMS_MANAGED ,
443
464
}
444
465
) ;
445
466
@@ -449,12 +470,16 @@ export class CdkTextractStack extends cdk.Stack {
449
470
{
450
471
visibilityTimeout : cdk . Duration . seconds ( 900 ) ,
451
472
retentionPeriod : cdk . Duration . seconds ( 1209600 ) ,
473
+ encryption : QueueEncryption . KMS ,
474
+ encryptionMasterKey : jobResultsKey ,
475
+ dataKeyReuse : cdk . Duration . seconds ( 86400 ) ,
452
476
deadLetterQueue : {
453
477
maxReceiveCount : 3 ,
454
478
queue : jobResultsDLQueue ,
455
479
} ,
456
480
}
457
481
) ;
482
+
458
483
// trigger
459
484
jobCompletionTopic . addSubscription (
460
485
new snsSubscriptions . SqsSubscription ( jobResultsQueue )
@@ -874,6 +899,7 @@ export class CdkTextractStack extends cdk.Stack {
874
899
jobResultProcessor . addLayers ( textractorLayer ) ;
875
900
jobResultProcessor . addLayers ( boto3Layer ) ;
876
901
jobResultProcessor . addLayers ( elasticSearchLayer ) ;
902
+ jobResultsKey . grantEncryptDecrypt ( jobResultProcessor ) ;
877
903
878
904
// Triggers
879
905
jobResultProcessor . addEventSource (
0 commit comments