Skip to content

Commit ab78756

Browse files
davidfsmithclaude
andcommitted
Switch infra to SSM reads and remove T&C CDK infrastructure
Requires PR 1 (feat/ssm-cross-stack-sharing) to be deployed first so the 14 SSM parameters exist when CloudFormation validates infra's changeset. Infrastructure stack: - Reads all 14 shared values from SSM via valueForStringParameter() - No longer accepts direct cross-stack props beyond baseStackName - Eliminates all Fn::ImportValue dependencies on BaseStack - IEventBus type used across all 11 domain constructs (compatible with EventBus.fromEventBusArn() which returns IEventBus) BaseStack: - Removes T&C CloudFront distribution and S3 bucket (tacCdn) - Retains all 14 SSM parameters from PR 1 Pipeline stage: - Passes only baseStackName to DeepracerEventManagerStack - MIGRATION ordering: baseStack.addDependency(stack) (infra first) so infra drops Fn::ImportValue before base removes CfnOutput exports. SSM params already exist from PR 1 so infra can resolve them. Revert to stack.addDependency(baseStack) after this PR is deployed. - Removes TermsAndConditionsDeployToS3 pipeline step Also removes website-terms-and-conditions/ directory and all remaining T&C CDK references from bin/drem.ts. CDK tests: all 4 pass (SSM params in base, no Fn::ImportValue in infra, SSM parameter types used in infra template). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 800dec8 commit ab78756

17 files changed

+205
-180
lines changed

CLAUDE.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,20 @@ pytest # Run Python tests
8080
### Public Frontends
8181
- **Leaderboard** (`website-leaderboard/`) — unauthenticated, subscribes to AppSync via API key for live data; supports URL query params for display (lang, QR, track, format, flag)
8282
- **Stream Overlays** (`website-stream-overlays/`) — unauthenticated, API key auth; uses D3.js for animated visualisations; supports chroma key background for broadcast use
83+
84+
### Cross-Stack Sharing Pattern
85+
86+
`BaseStack` and `DeepracerEventManagerStack` share values exclusively via **SSM Parameter Store** — there are no CloudFormation `Fn::ImportValue` / `CfnOutput` cross-stack references between them. This means either stack can be updated independently and in any order without CloudFormation blocking on export dependencies.
87+
88+
**How it works:**
89+
- `BaseStack` writes all shared resource identifiers to SSM under `/${stackName}/<key>` at the end of its constructor
90+
- `DeepracerEventManagerStack` reads them via `ssm.StringParameter.valueForStringParameter()` (resolved at CloudFormation deploy time, not synth time) and reconstructs CDK objects using `from*` static methods (`Role.fromRoleArn`, `EventBus.fromEventBusArn`, `Bucket.fromBucketName`, etc.)
91+
- `DeepracerEventManagerStack` only takes `baseStackName: string` as a cross-stack prop
92+
93+
**When adding new resources to BaseStack that InfrastructureStack needs:**
94+
1. Write the resource identifier to SSM in `BaseStack` constructor
95+
2. Read it with `valueForStringParameter` in `DeepracerEventManagerStack` and reconstruct the CDK object
96+
3. Do NOT pass it as a constructor prop — this would recreate the `Fn::ImportValue` dependency
97+
98+
**When removing resources from BaseStack:**
99+
Because there are no `Fn::ImportValue` dependencies, you can remove a resource from `BaseStack` and deploy in a single pipeline run without the "cannot delete export in use" error. Ensure `InfrastructureStack` no longer reads the corresponding SSM parameter in the same commit.

bin/drem.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -86,22 +86,6 @@ if (app.node.tryGetContext('manual_deploy') === 'True') {
8686

8787
new DeepracerEventManagerStack(app, `drem-backend-${labelName}-infrastructure`, {
8888
baseStackName: baseStack.stackName,
89-
cloudfrontDistribution: baseStack.cloudfrontDistribution,
90-
cloudfrontDomainNames: baseStack.cloudfrontDomainNames,
91-
tacCloudfrontDistribution: baseStack.tacCloudfrontDistribution,
92-
tacSourceBucket: baseStack.tacSourceBucket,
93-
logsBucket: baseStack.logsBucket,
94-
lambdaConfig: baseStack.lambdaConfig,
95-
adminGroupRole: baseStack.idp.adminGroupRole,
96-
operatorGroupRole: baseStack.idp.operatorGroupRole,
97-
commentatorGroupRole: baseStack.idp.commentatorGroupRole,
98-
registrationGroupRole: baseStack.idp.registrationGroupRole,
99-
authenticatedUserRole: baseStack.idp.authenticatedUserRole,
100-
userPool: baseStack.idp.userPool,
101-
identiyPool: baseStack.idp.identityPool,
102-
userPoolClientWeb: baseStack.idp.userPoolClientWeb,
103-
dremWebsiteBucket: baseStack.dremWebsitebucket,
104-
eventbus: baseStack.eventbridge.eventbus,
10589
env: env,
10690
});
10791
} else {

lib/base-stack.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@ export class BaseStack extends cdk.Stack {
2828
public readonly idp: Idp;
2929
public readonly cloudfrontDistribution: Distribution;
3030
public readonly cloudfrontDomainNames?: string[];
31-
public readonly tacSourceBucket: s3.Bucket;
32-
public readonly tacCloudfrontDistribution: Distribution;
3331
public readonly logsBucket: s3.Bucket;
3432
public readonly lambdaConfig: {
3533
runtime: awsLambda.Runtime;
@@ -107,6 +105,7 @@ export class BaseStack extends cdk.Stack {
107105
logsBucket: logsBucket,
108106
domainNames: siteDomain,
109107
certificate: certificate,
108+
comment: 'DREM main website',
110109
});
111110
this.cloudfrontDistribution = cdn.distribution;
112111

@@ -118,22 +117,6 @@ export class BaseStack extends cdk.Stack {
118117
zone: hostedZone,
119118
});
120119
}
121-
// Terms And Conditions webpage
122-
const tacWebsite = new Website(this, 'TermsNConditions', {
123-
contentPath: './website-terms-and-conditions/',
124-
pathPattern: '/terms-and-conditions.html',
125-
logsBucket: logsBucket,
126-
cdnDistribution: cdn.distribution,
127-
});
128-
this.tacSourceBucket = tacWebsite.sourceBucket;
129-
130-
// Terms And Conditions cloudfront Distribution
131-
const tacCdn = new Cdn(this, 'tacCdn', {
132-
defaultOrigin: tacWebsite.origin,
133-
logsBucket: logsBucket,
134-
});
135-
this.tacCloudfrontDistribution = tacCdn.distribution;
136-
137120
// Lambda
138121
// Common Config
139122
const lambda_architecture = awsLambda.Architecture.ARM_64;

lib/cdk-pipeline-stack.ts

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ class InfrastructurePipelineStage extends Stage {
2929
public readonly leaderboardSourceBucketName: cdk.CfnOutput;
3030
public readonly streamingOverlayDistributionId: cdk.CfnOutput;
3131
public readonly streamingOverlaySourceBucketName: cdk.CfnOutput;
32-
public readonly termAndConditionsDistributionId: cdk.CfnOutput;
33-
public readonly termAndConditionsSourceBucketName: cdk.CfnOutput;
3432

3533
constructor(scope: Construct, id: string, props: InfrastructurePipelineStageProps) {
3634
super(scope, id, props);
@@ -42,32 +40,19 @@ class InfrastructurePipelineStage extends Stage {
4240
});
4341
const stack = new DeepracerEventManagerStack(this, 'infrastructure', {
4442
baseStackName: baseStack.stackName,
45-
cloudfrontDistribution: baseStack.cloudfrontDistribution,
46-
cloudfrontDomainNames: baseStack.cloudfrontDomainNames,
47-
tacCloudfrontDistribution: baseStack.tacCloudfrontDistribution,
48-
tacSourceBucket: baseStack.tacSourceBucket,
49-
logsBucket: baseStack.logsBucket,
50-
lambdaConfig: baseStack.lambdaConfig,
51-
adminGroupRole: baseStack.idp.adminGroupRole,
52-
operatorGroupRole: baseStack.idp.operatorGroupRole,
53-
commentatorGroupRole: baseStack.idp.commentatorGroupRole,
54-
registrationGroupRole: baseStack.idp.registrationGroupRole,
55-
authenticatedUserRole: baseStack.idp.authenticatedUserRole,
56-
userPool: baseStack.idp.userPool,
57-
identiyPool: baseStack.idp.identityPool,
58-
userPoolClientWeb: baseStack.idp.userPoolClientWeb,
59-
dremWebsiteBucket: baseStack.dremWebsitebucket,
60-
eventbus: baseStack.eventbridge.eventbus,
6143
});
44+
// MIGRATION: infrastructure deploys before base so infra can drop
45+
// Fn::ImportValue references while base still exports them. SSM parameters
46+
// already exist from PR 1 so infra can resolve them at changeset time.
47+
// Revert to stack.addDependency(baseStack) after this PR is deployed.
48+
baseStack.addDependency(stack);
6249

6350
this.distributionId = stack.distributionId;
6451
this.sourceBucketName = stack.sourceBucketName;
6552
this.leaderboardSourceBucketName = stack.leaderboardSourceBucketName;
6653
this.leaderboardDistributionId = stack.leaderboardDistributionId;
6754
this.streamingOverlaySourceBucketName = stack.streamingOverlaySourceBucketName;
6855
this.streamingOverlayDistributionId = stack.streamingOverlayDistributionId;
69-
this.termAndConditionsSourceBucketName = stack.tacSourceBucketName;
70-
this.termAndConditionsDistributionId = stack.tacWebsitedistributionId;
7156
}
7257
}
7358
export interface CdkPipelineStackProps extends cdk.StackProps {
@@ -300,28 +285,6 @@ export class CdkPipelineStack extends cdk.Stack {
300285
})
301286
);
302287

303-
// Terms and Conditions website Deploy to S3
304-
infrastructure_stage.addPost(
305-
new pipelines.CodeBuildStep('TermsAndConditionsDeployToS3', {
306-
buildEnvironment: {
307-
privileged: false,
308-
computeType: codebuild.ComputeType.LARGE,
309-
},
310-
commands: [
311-
// configure and deploy Terms and Conditions website
312-
"echo 'Starting to deploy the Terms and Conditions website'",
313-
'echo website bucket= $sourceBucketName',
314-
'aws s3 sync ./website-terms-and-conditions/ s3://$sourceBucketName/ --delete',
315-
"aws cloudfront create-invalidation --distribution-id $distributionId --paths '/*'",
316-
],
317-
envFromCfnOutputs: {
318-
sourceBucketName: infrastructure.termAndConditionsSourceBucketName,
319-
distributionId: infrastructure.termAndConditionsDistributionId,
320-
},
321-
rolePolicyStatements: rolePolicyStatementsForWebsiteDeployStages,
322-
})
323-
);
324-
325288
pipeline.buildPipeline();
326289
const topic = new sns.Topic(this, 'PipelineTopic');
327290
topic.addSubscription(new subs.EmailSubscription(props.email));

lib/constructs/car-logs-manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
66
import * as ec2 from 'aws-cdk-lib/aws-ec2';
77
import { SubnetType } from 'aws-cdk-lib/aws-ec2';
88
import * as ecr_assets from 'aws-cdk-lib/aws-ecr-assets';
9-
import { EventBus, Rule } from 'aws-cdk-lib/aws-events';
9+
import { IEventBus, Rule } from 'aws-cdk-lib/aws-events';
1010
import { LambdaFunction } from 'aws-cdk-lib/aws-events-targets';
1111
import * as iam from 'aws-cdk-lib/aws-iam';
1212
import * as lambda from 'aws-cdk-lib/aws-lambda';
@@ -50,7 +50,7 @@ export interface CarLogsManagerProps {
5050
powerToolsLayer: lambda.ILayerVersion;
5151
};
5252
};
53-
eventbus: EventBus;
53+
eventbus: IEventBus;
5454
}
5555

5656
export class CarLogsManager extends Construct {

lib/constructs/cars-manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { DockerImage, Duration, RemovalPolicy } from 'aws-cdk-lib';
44
import * as appsync from 'aws-cdk-lib/aws-appsync';
55
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
66
import * as awsEvents from 'aws-cdk-lib/aws-events';
7-
import { EventBus } from 'aws-cdk-lib/aws-events';
7+
import { IEventBus } from 'aws-cdk-lib/aws-events';
88
import * as awsEventsTargets from 'aws-cdk-lib/aws-events-targets';
99
import * as iam from 'aws-cdk-lib/aws-iam';
1010
import { ManagedPolicy, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
@@ -36,7 +36,7 @@ export interface CarManagerProps {
3636
appsyncHelpersLayer: lambda.ILayerVersion;
3737
};
3838
};
39-
eventbus: EventBus;
39+
eventbus: IEventBus;
4040
}
4141

4242
export class CarManager extends Construct {

lib/constructs/clamscan-serverless.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DockerImage, Duration, RemovalPolicy, Size } from 'aws-cdk-lib';
22
import * as appsync from 'aws-cdk-lib/aws-appsync';
33
import { Platform } from 'aws-cdk-lib/aws-ecr-assets';
4-
import { EventBus, Match, Rule, Schedule } from 'aws-cdk-lib/aws-events';
4+
import { IEventBus, Match, Rule, Schedule } from 'aws-cdk-lib/aws-events';
55
import { LambdaFunction } from 'aws-cdk-lib/aws-events-targets';
66
import * as lambda from 'aws-cdk-lib/aws-lambda';
77
import { EventBridgeDestination } from 'aws-cdk-lib/aws-lambda-destinations';
@@ -34,7 +34,7 @@ interface ClamscanServerlessProps {
3434
powerToolsLayer: lambda.ILayerVersion;
3535
};
3636
};
37-
eventbus: EventBus;
37+
eventbus: IEventBus;
3838
}
3939

4040
export class ClamscanServerless extends Construct {

lib/constructs/events-manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DockerImage, Duration, RemovalPolicy, Stack } from 'aws-cdk-lib';
22
import * as appsync from 'aws-cdk-lib/aws-appsync';
33
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
4-
import { EventBus } from 'aws-cdk-lib/aws-events';
4+
import { IEventBus } from 'aws-cdk-lib/aws-events';
55
import * as lambda from 'aws-cdk-lib/aws-lambda';
66
import { StartingPosition } from 'aws-cdk-lib/aws-lambda';
77
import { DynamoEventSource } from 'aws-cdk-lib/aws-lambda-event-sources';
@@ -43,7 +43,7 @@ export interface EventsManagerProps {
4343
landingPageConfigObjectType: ObjectType;
4444
landingPageConfigInputType: InputType;
4545
};
46-
eventbus: EventBus;
46+
eventbus: IEventBus;
4747
}
4848
export class EventsManager extends Construct {
4949
constructor(scope: Construct, id: string, props: EventsManagerProps) {

lib/constructs/fleets-manager.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DockerImage, Duration, RemovalPolicy } from 'aws-cdk-lib';
22
import * as appsync from 'aws-cdk-lib/aws-appsync';
33
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
4-
import { EventBus } from 'aws-cdk-lib/aws-events';
4+
import { IEventBus } from 'aws-cdk-lib/aws-events';
55
import * as lambda from 'aws-cdk-lib/aws-lambda';
66
import { CodeFirstSchema, Directive, GraphqlType, ObjectType, ResolvableField } from 'awscdk-appsync-utils';
77
import { NagSuppressions } from 'cdk-nag';
@@ -27,7 +27,7 @@ export interface FleetsManagerProps {
2727
appsyncHelpersLayer: lambda.ILayerVersion;
2828
};
2929
};
30-
eventbus: EventBus;
30+
eventbus: IEventBus;
3131
}
3232

3333
export class FleetsManager extends Construct {

lib/constructs/landing-page.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DockerImage, Duration, RemovalPolicy } from 'aws-cdk-lib';
22
import * as appsync from 'aws-cdk-lib/aws-appsync';
33
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
4-
import { EventBus, Rule } from 'aws-cdk-lib/aws-events';
4+
import { IEventBus, Rule } from 'aws-cdk-lib/aws-events';
55
import { IRole } from 'aws-cdk-lib/aws-iam';
66
import * as lambda from 'aws-cdk-lib/aws-lambda';
77
import { StandardLambdaPythonFunction } from './standard-lambda-python-function';
@@ -35,7 +35,7 @@ export interface LandingPageManagerProps {
3535
powerToolsLayer: lambda.ILayerVersion;
3636
};
3737
};
38-
eventbus: EventBus;
38+
eventbus: IEventBus;
3939
}
4040
export class LandingPageManager extends Construct {
4141
public readonly api: {

0 commit comments

Comments
 (0)