Skip to content

Commit

Permalink
CMDCT-4194 cdk side, creating a better way to import resources from s…
Browse files Browse the repository at this point in the history
…erverless including Cognito UserPool (#15012)
  • Loading branch information
peoplespete committed Dec 31, 2024
1 parent 3b6eee7 commit c8a2a23
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 126 deletions.
83 changes: 44 additions & 39 deletions deployment/app.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,62 @@
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { WithoutImportsParentStack } from "./stacks/without_imports/parent";
import { WithImportsParentStack} from "./stacks/with_imports/parent";
import { ParentStack } from "./stacks/parent";
import { determineDeploymentConfig } from "./deployment-config";
import { getSecret } from "./utils/secrets-manager";
import { getDeploymentConfigParameters } from "./utils/systems-manager";

async function main() {
try {
const app = new cdk.App({
defaultStackSynthesizer: new cdk.DefaultStackSynthesizer(
JSON.parse((await getSecret("cdkSynthesizerConfig"))!)
),
});
const app = new cdk.App({
defaultStackSynthesizer: new cdk.DefaultStackSynthesizer(
JSON.parse((await getSecret("cdkSynthesizerConfig"))!)
),
});

const stage = app.node.getContext("stage");
const config = await determineDeploymentConfig(stage);
const stage = app.node.getContext("stage");
const config = await determineDeploymentConfig(stage);

const parametersToFetch = {
cloudfrontCertificateArn: {
name: "cloudfront/certificateArn",
useDefault: true,
},
cloudfrontDomainName: {
name: "cloudfront/domainName",
useDefault: false,
},
vpnIpSetArn: { name: "vpnIpSetArn", useDefault: true },
vpnIpv6SetArn: { name: "vpnIpv6SetArn", useDefault: true },
hostedZoneId: { name: "route53/hostedZoneId", useDefault: true },
domainName: { name: "route53/domainName", useDefault: true },
};
const parametersToFetch = {
cloudfrontCertificateArn: {
name: "cloudfront/certificateArn",
useDefault: true,
},
cloudfrontDomainName: {
name: "cloudfront/domainName",
useDefault: false,
},
vpnIpSetArn: { name: "vpnIpSetArn", useDefault: true },
vpnIpv6SetArn: { name: "vpnIpv6SetArn", useDefault: true },
hostedZoneId: { name: "route53/hostedZoneId", useDefault: true },
domainName: { name: "route53/domainName", useDefault: true },
};

const deploymentConfigParameters = await getDeploymentConfigParameters(
parametersToFetch,
stage
);
const deploymentConfigParameters = await getDeploymentConfigParameters(
parametersToFetch,
stage
);

cdk.Tags.of(app).add("STAGE", stage);
cdk.Tags.of(app).add("PROJECT", config.project);
cdk.Tags.of(app).add("STAGE", stage);
cdk.Tags.of(app).add("PROJECT", config.project);

new ParentStack(app, `${config.project}-${stage}`, {
...config,
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
deploymentConfigParameters,
});
} catch (error) {
console.error("Error:", error);
process.exit(1);
let correctParentStack;
if (process.env.WITHOUT_IMPORTS) {
correctParentStack = WithoutImportsParentStack
} else if (process.env.WITH_IMPORTS) {
correctParentStack = WithImportsParentStack
} else {
correctParentStack = ParentStack
}
new correctParentStack(app, `${config.project}-${stage}`, {
...config,
env: {
account: process.env.CDK_DEFAULT_ACCOUNT,
region: process.env.CDK_DEFAULT_REGION,
},
deploymentConfigParameters,
});
}

main();
46 changes: 25 additions & 21 deletions deployment/import_instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,54 +5,58 @@
1. Deploy sls to get it ready for deletion with retained resources configured for import

```
./run deploy --stage master
./run deploy --stage <YOUR_BRANCH_NAME>
```

2. Destroy sls
2. Collect information about the resources we're going to be importing into the new cdk stack.
```
cloudfront.Distribution -
cognito.UserPool -
```

3. Destroy sls

```
./run destroy --stage master
./run destroy --stage <YOUR_BRANCH_NAME>
```

## From `jon-cdk` branch:

1. Comment out Cloudfront Distribution definition and dependent resources

```
# ONLY RUN ONCE YOU COMMENTED THEM OUT
./run deploy --stage master
1. Create just the new cdk stack without anything inside of it.

```bash
WITHOUT_IMPORTS=true ./run deploy --stage <YOUR_BRANCH_NAME>
```

2. Restore Cloudfront Distribution definition in the simplified version (there are 2)
2. Now import all the serverless ejected resources.

```bash
WITH_IMPORTS=true PROJECT=seds cdk import --context stage=<YOUR_BRANCH_NAME> --force
```
PROJECT=seds cdk import --context stage=master --force
```
As this import occurs you'll have to provide the information you gathered just before destroying the serverless stacks.

3. Answer questions as you import to make sure you get the SLS UI stack's retained Cloudfront Distribution
3. Run a deploy on that same imported resource set.

4. Run a cdk deploy

```
./run deploy --stage master
```bash
WITH_IMPORTS=true ./run deploy --stage <YOUR_BRANCH_NAME>
```

5. Comment out the simplified version of Cloudfront Distribution definition. Restore the complicated Cloudfront Distribution definition and dependent resources
4. Run a full deploy by kicking off the full cdk deploy via Github Action. Permissions for individual developers are limited so you must use Github Action to do this part.

6. Run a cdk deploy again
5. Find the Cloudfront Url in the Github Action's logs (or in the outputs section of your Cloudformation Stack). Visit the site and confirm that you can login and use the application. :tada: Congrats, you did it!

```
./run deploy --stage master
```

## What if it all goes pear shaped?

### If during the middle of the migration, things begin to break and we need to reinstate the serverless stack, we need a way to bring the Cloudfront Distribution back into the newly rebuilt serverless stack. Fortunately this is possible if you follow these steps.

:grey_exclamation: These instructions are specific to reimporting a Cloudfront Distribution but the same pattern should also apply to any other imported resources that need un-importing should the need arise.

1) Get the Cloudfront Distribution unaffiliated with any Cloudformation stack. If it's already been successfully imported into the new cdk stack then you'll need to destroy the cdk stack to eject it from that stack.
```
# this assumes you're on `jon-cdk` branch
./run destroy --stage master
./run destroy --stage <YOUR_BRANCH_NAME>
```

2) Now you need switch to `pete-sls` branch and comment out any CloudfrontDistribution and dependent configuration.
Expand Down
35 changes: 19 additions & 16 deletions deployment/stacks/data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,26 +79,29 @@ export function createDataComponents(props: CreateDataComponentsProps) {
"service-role/AWSLambdaVPCAccessExecutionRole"
),
],
inlinePolicies: {
DynamoPolicy: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"dynamodb:DescribeTable",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
],
resources: ["*"],
})
]
})
},
permissionsBoundary: props.iamPermissionsBoundary,
path: props.iamPath,
});

lambdaApiRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"dynamodb:DescribeTable",
"dynamodb:Query",
"dynamodb:Scan",
"dynamodb:GetItem",
"dynamodb:PutItem",
"dynamodb:UpdateItem",
"dynamodb:DeleteItem",
],
resources: ["*"],
})
);

// TODO: test deploy and watch performance with this using lambda.Function vs lambda_nodejs.NodejsFunction
const seedDataFunction = new lambda_nodejs.NodejsFunction(scope, "seedData", {
entry: "services/database/handlers/seed/seed.js",
Expand Down
55 changes: 25 additions & 30 deletions deployment/stacks/ui-auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
aws_lambda_nodejs as lambda_nodejs,
aws_wafv2 as wafv2,
aws_ssm as ssm,
RemovalPolicy,
Aws,
Duration,
custom_resources as cr,
Expand Down Expand Up @@ -40,7 +39,6 @@ export function createUiAuthComponents(props: CreateUiAuthComponentsProps) {

const userPool = new cognito.UserPool(scope, "UserPool", {
userPoolName: `${stage}-user-pool`,
removalPolicy: RemovalPolicy.DESTROY,
signInAliases: {
email: true,
},
Expand Down Expand Up @@ -208,36 +206,33 @@ export function createUiAuthComponents(props: CreateUiAuthComponentsProps) {
"service-role/AWSLambdaVPCAccessExecutionRole"
),
],
inlinePolicies: {
LambdaApiRolePolicy: new iam.PolicyDocument({
statements: [
new iam.PolicyStatement({
actions: [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
resources: ["arn:aws:logs:*:*:*"],
effect: iam.Effect.ALLOW,
}),
new iam.PolicyStatement({
actions: ["*"],
resources: [userPool.userPoolArn],
effect: iam.Effect.ALLOW,
}),
new iam.PolicyStatement({
actions: ["ssm:GetParameter"],
resources: [bootstrapUsersPasswordArn],
effect: iam.Effect.ALLOW,
}),
],
}),
},
});

lambdaApiRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents",
],
resources: ["arn:aws:logs:*:*:*"],
})
);

lambdaApiRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["*"],
resources: [userPool.userPoolArn],
})
);

lambdaApiRole.addToPolicy(
new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["ssm:GetParameter"],
resources: [bootstrapUsersPasswordArn],
})
);

// TODO: test deploy and watch performance with scope using lambda.Function vs lambda_nodejs.NodejsFunction
bootstrapUsersFunction = new lambda_nodejs.NodejsFunction(
scope,
Expand Down
14 changes: 0 additions & 14 deletions deployment/stacks/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,20 +93,6 @@ export function createUiComponents(props: CreateUiComponentsProps) {
}
);

// new cloudfront.Distribution(
// scope,
// 'CloudFrontDistribution',
// {
// defaultBehavior: {
// origin: new cloudfrontOrigins.HttpOrigin(
// 'www.example.com',
// { originId: 'Default'}
// ),
// cachePolicy: cloudfront.CachePolicy.CACHING_DISABLED,
// },
// }
// );

const distribution = new cloudfront.Distribution(
scope,
"CloudFrontDistribution",
Expand Down
28 changes: 28 additions & 0 deletions deployment/stacks/with_imports/parent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { Construct } from "constructs";
import {
Stack,
StackProps,
} from "aws-cdk-lib";
import { DeploymentConfigProperties } from "../../deployment-config";
import { createUiComponents } from "./ui";
import { createUiAuthComponents } from "./ui-auth";

export class WithImportsParentStack extends Stack {
constructor(
scope: Construct,
id: string,
props: StackProps & DeploymentConfigProperties
) {
super(scope, id, props);

const {
stage,
} = props;

createUiComponents({scope: this});
createUiAuthComponents({
scope: this,
stage,
});
}
}
Loading

0 comments on commit c8a2a23

Please sign in to comment.