@@ -11,7 +11,9 @@ import {
1111 PolicyStatement ,
1212 AnyPrincipal ,
1313 AccountRootPrincipal ,
14- PolicyDocument
14+ PolicyDocument ,
15+ Role ,
16+ ServicePrincipal
1517} from "aws-cdk-lib/aws-iam"
1618import { Key , Alias } from "aws-cdk-lib/aws-kms"
1719import { Duration } from "aws-cdk-lib"
@@ -37,6 +39,48 @@ export class Tables extends Construct {
3739
3840 const { account, region} = Stack . of ( this )
3941
42+ const dynamoDbScalingRolePolicy = props . enableDynamoDBAutoScaling
43+ ? new ManagedPolicy ( this , "DynamoDbScalingRolePolicy" , {
44+ statements : [
45+ new PolicyStatement ( {
46+ actions : [
47+ "dynamodb:DescribeTable" ,
48+ "dynamodb:UpdateTable"
49+ ] ,
50+ resources : [
51+ `arn:aws:dynamodb:${ region } :${ account } :table/${ props . stackName } -*`
52+ ]
53+ } ) ,
54+ new PolicyStatement ( {
55+ actions : [
56+ "cloudwatch:PutMetricAlarm" ,
57+ "cloudwatch:DescribeAlarms" ,
58+ "cloudwatch:DeleteAlarms"
59+ ] ,
60+ resources : [ "*" ]
61+ } )
62+ ]
63+ } )
64+ : undefined
65+
66+ if ( dynamoDbScalingRolePolicy ) {
67+ NagSuppressions . addResourceSuppressions ( dynamoDbScalingRolePolicy , [
68+ {
69+ id : "AwsSolutions-IAM5" ,
70+ reason :
71+ "Stack-scoped wildcard is required for autoscaling permissions " +
72+ "across stateful DynamoDB tables and indexes."
73+ }
74+ ] )
75+ }
76+
77+ const dynamoDbScalingRole = props . enableDynamoDBAutoScaling
78+ ? new Role ( this , "DynamoDbScalingRole" , {
79+ assumedBy : new ServicePrincipal ( "dynamodb.application-autoscaling.amazonaws.com" ) ,
80+ managedPolicies : dynamoDbScalingRolePolicy ? [ dynamoDbScalingRolePolicy ] : [ ]
81+ } )
82+ : undefined
83+
4084 // ── Shared autoscaling IAM role ──────────────────────────────────────────
4185 // Used by ApplicationAutoScaling when enableDynamoDBAutoScaling is true.
4286 // Equivalent to DynamoDbScalingRole in SAMtemplates/tables/main.yaml.
@@ -204,9 +248,14 @@ export class Tables extends Construct {
204248 }
205249
206250 if ( props . enableDynamoDBAutoScaling ) {
251+ const scalingRoleOptions = dynamoDbScalingRole
252+ ? { role : dynamoDbScalingRole }
253+ : { }
254+
207255 const tableWriteScaling = prescriptionStatusUpdatesTable . autoScaleWriteCapacity ( {
208256 minCapacity : MIN_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY ,
209- maxCapacity : MAX_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY
257+ maxCapacity : MAX_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY ,
258+ ...scalingRoleOptions
210259 } )
211260 tableWriteScaling . scaleOnUtilization ( {
212261 targetUtilizationPercent : 50 ,
@@ -216,7 +265,8 @@ export class Tables extends Construct {
216265
217266 const tableReadScaling = prescriptionStatusUpdatesTable . autoScaleReadCapacity ( {
218267 minCapacity : 1 ,
219- maxCapacity : 100
268+ maxCapacity : 100 ,
269+ ...scalingRoleOptions
220270 } )
221271 tableReadScaling . scaleOnUtilization ( {
222272 targetUtilizationPercent : 70 ,
@@ -228,7 +278,8 @@ export class Tables extends Construct {
228278 prescriptionStatusUpdatesTable . autoScaleGlobalSecondaryIndexWriteCapacity (
229279 "PharmacyODSCodePrescriptionIDIndex" , {
230280 minCapacity : MIN_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY ,
231- maxCapacity : MAX_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY
281+ maxCapacity : MAX_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY ,
282+ ...scalingRoleOptions
232283 } )
233284 pharmacyIndexWriteScaling . scaleOnUtilization ( {
234285 targetUtilizationPercent : 50 ,
@@ -240,13 +291,66 @@ export class Tables extends Construct {
240291 prescriptionStatusUpdatesTable . autoScaleGlobalSecondaryIndexReadCapacity (
241292 "PharmacyODSCodePrescriptionIDIndex" , {
242293 minCapacity : 1 ,
243- maxCapacity : 100
294+ maxCapacity : 100 ,
295+ ...scalingRoleOptions
244296 } )
245297 pharmacyIndexReadScaling . scaleOnUtilization ( {
246298 targetUtilizationPercent : 70 ,
247299 scaleInCooldown : Duration . seconds ( 60 ) ,
248300 scaleOutCooldown : Duration . seconds ( 60 )
249301 } )
302+
303+ const nhsNumberIndexWriteScaling =
304+ prescriptionStatusUpdatesTable . autoScaleGlobalSecondaryIndexWriteCapacity (
305+ "PatientNHSNumberIndex" , {
306+ minCapacity : MIN_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY ,
307+ maxCapacity : MAX_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY ,
308+ ...scalingRoleOptions
309+ } )
310+ nhsNumberIndexWriteScaling . scaleOnUtilization ( {
311+ targetUtilizationPercent : 50 ,
312+ scaleInCooldown : Duration . seconds ( 600 ) ,
313+ scaleOutCooldown : Duration . seconds ( 0 )
314+ } )
315+
316+ const nhsNumberIndexReadScaling =
317+ prescriptionStatusUpdatesTable . autoScaleGlobalSecondaryIndexReadCapacity (
318+ "PatientNHSNumberIndex" , {
319+ minCapacity : 1 ,
320+ maxCapacity : 100 ,
321+ ...scalingRoleOptions
322+ } )
323+ nhsNumberIndexReadScaling . scaleOnUtilization ( {
324+ targetUtilizationPercent : 70 ,
325+ scaleInCooldown : Duration . seconds ( 60 ) ,
326+ scaleOutCooldown : Duration . seconds ( 60 )
327+ } )
328+
329+ const pharmacyIndexIncPostDatedWriteScaling =
330+ prescriptionStatusUpdatesTable . autoScaleGlobalSecondaryIndexWriteCapacity (
331+ "PharmacyODSCodePrescriptionIDIndexIncPostDated" , {
332+ minCapacity : MIN_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY ,
333+ maxCapacity : MAX_WRITE_PRESCRIPTION_STATUS_UPDATES_CAPACITY ,
334+ ...scalingRoleOptions
335+ } )
336+ pharmacyIndexIncPostDatedWriteScaling . scaleOnUtilization ( {
337+ targetUtilizationPercent : 50 ,
338+ scaleInCooldown : Duration . seconds ( 600 ) ,
339+ scaleOutCooldown : Duration . seconds ( 0 )
340+ } )
341+
342+ const pharmacyIndexIncPostDatedReadScaling =
343+ prescriptionStatusUpdatesTable . autoScaleGlobalSecondaryIndexReadCapacity (
344+ "PharmacyODSCodePrescriptionIDIndexIncPostDated" , {
345+ minCapacity : 1 ,
346+ maxCapacity : 100 ,
347+ ...scalingRoleOptions
348+ } )
349+ pharmacyIndexIncPostDatedReadScaling . scaleOnUtilization ( {
350+ targetUtilizationPercent : 70 ,
351+ scaleInCooldown : Duration . seconds ( 60 ) ,
352+ scaleOutCooldown : Duration . seconds ( 60 )
353+ } )
250354 }
251355
252356 const prescriptionStatusUpdatesReadPolicy = new ManagedPolicy (
@@ -417,10 +521,15 @@ export class Tables extends Construct {
417521 }
418522
419523 if ( props . enableDynamoDBAutoScaling ) {
524+ const scalingRoleOptions = dynamoDbScalingRole
525+ ? { role : dynamoDbScalingRole }
526+ : { }
527+
420528 const notifStatesTableWriteScaling =
421529 prescriptionNotificationStatesTable . autoScaleWriteCapacity ( {
422530 minCapacity : MIN_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY ,
423- maxCapacity : MAX_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY
531+ maxCapacity : MAX_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY ,
532+ ...scalingRoleOptions
424533 } )
425534 notifStatesTableWriteScaling . scaleOnUtilization ( {
426535 targetUtilizationPercent : 50 ,
@@ -431,7 +540,8 @@ export class Tables extends Construct {
431540 const notifStatesTableReadScaling =
432541 prescriptionNotificationStatesTable . autoScaleReadCapacity ( {
433542 minCapacity : 1 ,
434- maxCapacity : 100
543+ maxCapacity : 100 ,
544+ ...scalingRoleOptions
435545 } )
436546 notifStatesTableReadScaling . scaleOnUtilization ( {
437547 targetUtilizationPercent : 70 ,
@@ -443,7 +553,8 @@ export class Tables extends Construct {
443553 prescriptionNotificationStatesTable . autoScaleGlobalSecondaryIndexWriteCapacity (
444554 "PatientPharmacyIndex" , {
445555 minCapacity : MIN_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY ,
446- maxCapacity : MAX_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY
556+ maxCapacity : MAX_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY ,
557+ ...scalingRoleOptions
447558 } )
448559 patientPharmacyIndexWriteScaling . scaleOnUtilization ( {
449560 targetUtilizationPercent : 50 ,
@@ -455,7 +566,8 @@ export class Tables extends Construct {
455566 prescriptionNotificationStatesTable . autoScaleGlobalSecondaryIndexReadCapacity (
456567 "PatientPharmacyIndex" , {
457568 minCapacity : 1 ,
458- maxCapacity : 100
569+ maxCapacity : 100 ,
570+ ...scalingRoleOptions
459571 } )
460572 patientPharmacyIndexReadScaling . scaleOnUtilization ( {
461573 targetUtilizationPercent : 70 ,
@@ -467,7 +579,8 @@ export class Tables extends Construct {
467579 prescriptionNotificationStatesTable . autoScaleGlobalSecondaryIndexWriteCapacity (
468580 "NotifyMessageIDIndex" , {
469581 minCapacity : MIN_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY ,
470- maxCapacity : MAX_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY
582+ maxCapacity : MAX_WRITE_PRESCRIPTION_NOTIFICATION_STATES_CAPACITY ,
583+ ...scalingRoleOptions
471584 } )
472585 notifyMessageIdIndexWriteScaling . scaleOnUtilization ( {
473586 targetUtilizationPercent : 50 ,
@@ -479,7 +592,8 @@ export class Tables extends Construct {
479592 prescriptionNotificationStatesTable . autoScaleGlobalSecondaryIndexReadCapacity (
480593 "NotifyMessageIDIndex" , {
481594 minCapacity : 1 ,
482- maxCapacity : 100
595+ maxCapacity : 100 ,
596+ ...scalingRoleOptions
483597 } )
484598 notifyMessageIdIndexReadScaling . scaleOnUtilization ( {
485599 targetUtilizationPercent : 70 ,
0 commit comments