Skip to content

Commit

Permalink
Generate two CFN templates for create VPC and import VPC for bootstrap (
Browse files Browse the repository at this point in the history
opensearch-project#1113)

Changes functionality to generate two CFN templates for create VPC and import VPC for bootstrap. As well as introduces VPC Id and Subnet intake from CFN parameters that will later be used when deploying the Migration Assistant CDK

---------

Signed-off-by: Tanner Lewis <lewijacn@amazon.com>
Co-authored-by: Peter Nied <peternied@hotmail.com>
  • Loading branch information
lewijacn and peternied authored Nov 4, 2024
1 parent dc326f0 commit 5c7d575
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 16 deletions.
11 changes: 9 additions & 2 deletions deployment/migration-assistant-solution/bin/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,14 @@ const getProps = () => {

const app = new App();
const infraProps = getProps()
new SolutionsInfrastructureStack(app, 'OSMigrations-Bootstrap', {

new SolutionsInfrastructureStack(app, 'Migration-Assistant-Infra-Import-VPC', {
synthesizer: new DefaultStackSynthesizer(),
createVPC: false,
...infraProps
});
new SolutionsInfrastructureStack(app, 'Migration-Assistant-Infra-Create-VPC', {
synthesizer: new DefaultStackSynthesizer(),
createVPC: true,
...infraProps
});
});
86 changes: 76 additions & 10 deletions deployment/migration-assistant-solution/lib/solutions-stack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { Aws, CfnMapping, CfnParameter, Fn, Stack, StackProps, Tags } from 'aws-cdk-lib';
import { Construct } from 'constructs';
import {
Aws,
CfnMapping,
CfnParameter,
Fn,
Stack,
StackProps,
Tags
} from 'aws-cdk-lib';
import {Construct} from 'constructs';
import {
BlockDeviceVolume,
CloudFormationInit,
Expand All @@ -13,18 +21,23 @@ import {
MachineImage,
Vpc
} from "aws-cdk-lib/aws-ec2";
import { InstanceProfile, ManagedPolicy, Role, ServicePrincipal } from "aws-cdk-lib/aws-iam";
import { CfnDocument } from "aws-cdk-lib/aws-ssm";
import { Application, AttributeGroup } from "@aws-cdk/aws-servicecatalogappregistry-alpha";
import {InstanceProfile, ManagedPolicy, Role, ServicePrincipal} from "aws-cdk-lib/aws-iam";
import {CfnDocument} from "aws-cdk-lib/aws-ssm";
import {Application, AttributeGroup} from "@aws-cdk/aws-servicecatalogappregistry-alpha";

export interface SolutionsInfrastructureStackProps extends StackProps {
readonly solutionId: string;
readonly solutionName: string;
readonly solutionVersion: string;
readonly codeBucket: string;
readonly createVPC: boolean;
}

interface ParameterLabel {
default: string;
}

export function applyAppRegistry(stack: Stack, stage: string, infraProps: SolutionsInfrastructureStackProps): string {
function applyAppRegistry(stack: Stack, stage: string, infraProps: SolutionsInfrastructureStackProps): string {
const application = new Application(stack, "AppRegistry", {
applicationName: Fn.join("-", [
infraProps.solutionName,
Expand Down Expand Up @@ -62,6 +75,20 @@ export function applyAppRegistry(stack: Stack, stage: string, infraProps: Soluti
return application.applicationArn
}

function addParameterLabel(labels: Record<string, ParameterLabel>, parameter: CfnParameter, labelName: string) {
labels[parameter.logicalId] = {"default": labelName}
}

function importVPC(stack: Stack, vpdIdParameter: CfnParameter, availabilityZonesParameter: CfnParameter, privateSubnetIdsParameter: CfnParameter) {
const availabilityZones = availabilityZonesParameter.valueAsList
const privateSubnetIds = privateSubnetIdsParameter.valueAsList
return Vpc.fromVpcAttributes(stack, 'ImportedVPC', {
vpcId: vpdIdParameter.valueAsString,
availabilityZones: [Fn.select(0, availabilityZones)],
privateSubnetIds: [Fn.select(0, privateSubnetIds)]
});
}

export class SolutionsInfrastructureStack extends Stack {

constructor(scope: Construct, id: string, props: SolutionsInfrastructureStackProps) {
Expand All @@ -80,15 +107,18 @@ export class SolutionsInfrastructureStack extends Stack {
lazy: false,
});

const importedVPCParameters: string[] = [];
const additionalParameters: string[] = [];
const parameterLabels: Record<string, ParameterLabel> = {};
const stageParameter = new CfnParameter(this, 'Stage', {
type: 'String',
description: 'Specify the stage identifier which will be used in naming resources, e.g. dev,gamma,wave1',
default: 'dev',
});
additionalParameters.push(stageParameter.logicalId)

const stackMarker = `${stageParameter.valueAsString}-${Aws.REGION}`;
const appRegistryAppARN = applyAppRegistry(this, stackMarker, props)
const vpc = new Vpc(this, 'Vpc', {});

new CfnDocument(this, "BootstrapShellDoc", {
name: `BootstrapShellDoc-${stackMarker}`,
Expand Down Expand Up @@ -132,8 +162,37 @@ export class SolutionsInfrastructureStack extends Stack {
role: bootstrapRole
})

let vpc;
if (props.createVPC) {
vpc = new Vpc(this, 'Vpc', {});
}
else {
const vpcIdParameter = new CfnParameter(this, 'VPCId', {
type: 'AWS::EC2::VPC::Id',
description: 'Select a VPC, we recommend choosing the VPC of the target cluster.'
});
addParameterLabel(parameterLabels, vpcIdParameter, "VPC")

const availabilityZonesParameter = new CfnParameter(this, 'VPCAvailabilityZones', {
type: 'List<AWS::EC2::AvailabilityZone::Name>',
description: 'Select Availability Zones in the selected VPC. Please provide two zones at least, corresponding with the private subnets selected next.'
});
addParameterLabel(parameterLabels, availabilityZonesParameter, "Availability Zones")

const privateSubnetIdsParameter = new CfnParameter(this, 'VPCPrivateSubnetIds', {
type: 'List<AWS::EC2::Subnet::Id>',
description: 'Select Private Subnets in the selected VPC. Please provide two subnets at least, corresponding with the availability zones selected previously.'
});
addParameterLabel(parameterLabels, privateSubnetIdsParameter, "Private Subnets")
importedVPCParameters.push(vpcIdParameter.logicalId, availabilityZonesParameter.logicalId, privateSubnetIdsParameter.logicalId)
vpc = importVPC(this, vpcIdParameter, availabilityZonesParameter, privateSubnetIdsParameter);
}

new Instance(this, 'BootstrapEC2Instance', {
vpc: vpc,
vpcSubnets: {
subnets: vpc.privateSubnets,
},
instanceName: `bootstrap-instance-${stackMarker}`,
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.LARGE),
machineImage: MachineImage.latestAmazonLinux2023(),
Expand Down Expand Up @@ -161,9 +220,15 @@ export class SolutionsInfrastructureStack extends Stack {
}

const parameterGroups = [];
if (importedVPCParameters.length > 0) {
parameterGroups.push({
Label: { default: "Imported VPC parameters" },
Parameters: importedVPCParameters
});
}
parameterGroups.push({
Label: { default: "Additional parameters" },
Parameters: [stageParameter.logicalId]
Parameters: additionalParameters
});
parameterGroups.push({
Label: { default: "System parameters" },
Expand All @@ -172,8 +237,9 @@ export class SolutionsInfrastructureStack extends Stack {

this.templateOptions.metadata = {
'AWS::CloudFormation::Interface': {
ParameterGroups: parameterGroups
ParameterGroups: parameterGroups,
ParameterLabels: parameterLabels
}
}
}
}
}
2 changes: 1 addition & 1 deletion deployment/migration-assistant-solution/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"watch": "tsc -w",
"test": "npm run test:lint && npm run test:jest",
"test:lint": "eslint .",
"test:jest": "jest",
"test:jest": "jest --coverage",
"synth": "cdk synthesize --asset-metadata false --path-metadata false",
"deploy": "cdk deploy --require-approval never"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,34 @@ import { App } from 'aws-cdk-lib';
import { SolutionsInfrastructureStack } from '../lib/solutions-stack';

describe('Solutions stack', () => {
test('EC2 bootstrap instance is created', () => {
test('Generate migration assistant stack with create VPC', () => {
const app = new App();
const stack = new SolutionsInfrastructureStack(app, 'TestBootstrapStack', {
const stack = new SolutionsInfrastructureStack(app, 'TestMigrationAssistantStack', {
solutionId: 'SO0000',
solutionName: 'test-solution',
solutionVersion: '0.0.1',
codeBucket: 'test-bucket'
codeBucket: 'test-bucket',
createVPC: true
});
const template = Template.fromStack(stack);
template.resourceCountIs('AWS::EC2::VPC', 1)
template.resourceCountIs('AWS::ServiceCatalogAppRegistry::Application', 1)
template.hasResourceProperties('AWS::EC2::Instance', {
InstanceType: "t3.large"
});
});
test('Generate migration assistant stack with imported VPC', () => {
const app = new App();
const stack = new SolutionsInfrastructureStack(app, 'TestMigrationAssistantStack', {
solutionId: 'SO0000',
solutionName: 'test-solution',
solutionVersion: '0.0.1',
codeBucket: 'test-bucket',
createVPC: false
});
const template = Template.fromStack(stack);
template.resourceCountIs('AWS::EC2::VPC', 0)
template.resourceCountIs('AWS::ServiceCatalogAppRegistry::Application', 1)
template.hasResourceProperties('AWS::EC2::Instance', {
InstanceType: "t3.large"
});
Expand Down

0 comments on commit 5c7d575

Please sign in to comment.