@ndlib/ndlib-cdk2 1.0.29 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -362,9 +362,9 @@ new SourceWatcher(stack, 'TestProject', {
362
362
  NOTE: `webhookResourceStackName` refers to a stack which will manage contains the backend for a CustomResource webhook. Prior to using this construct, an instance of [ndlib/aws-github-webhook](https://github.com/ndlib/aws-github-webhook) should be deployed to the AWS account. One stack can be used for any number of SourceWatcher constructs.
363
363
 
364
364
 
365
- ## EC2 server with access to RDS
365
+ ## EC2 server with access rules
366
366
 
367
- The EC2withDatabase construct builds an EC2 server similar to those used in a large number of our services. The basic concept is to build the server with security group access to an AWS RDS database server. The server is built within an existing VPC. Parameters allow for AMI ID, instance type, root disk storage, IP address, networking, and security group rules for the server upon build. The server is created with the OS only; further configuration will need to be performed, often using ansible.
367
+ The EC2withDatabase construct builds an EC2 server. The basic concept is to build the server with security group access to, possibly, several AWS RDS database servers. The server is built within an existing VPC. Parameters allow for AMI ID, instance type, root disk storage, networking, and security group rules for the server upon build. The server is created with the OS only; further configuration will need to be performed, often using ansible.
368
368
 
369
369
  Example usage:
370
370
 
@@ -380,22 +380,28 @@ new EC2withDatabase (app, 'StackName', {
380
380
  env: { account: 'AccountID', region: 'us-east-1' },
381
381
  amiId: "Valid AMI Id",
382
382
  availabilityZones: [ "us-east-1c" ],
383
+ backup: 'True',
383
384
  instanceClass: ec2.InstanceClass.T3A,
384
385
  instanceSize: ec2.InstanceSize.MEDIUM,
385
386
  instanceName: "InstanceName",
386
387
  keyName: "libnd",
387
388
  privateIpAddress: "IPAddress",
388
- publicSubnetIds: [ "ValidSubnet in VPC" ],
389
+ publicSubnetIds: [ "ValidSubnet in VPC that matches up with AvailabilityZone" ],
389
390
  domainName: "Local domain name",
390
- cnameList: [ Array of cnames/additional IPs to be added ],
391
+ CnameList: [ Array of cnames/additional IPs to be added ],
391
392
  volumeSize: Number (in GB),
392
393
  vpcId: "Valid VPC ID",
393
- sg_ingress_db: 'Security Group of database for access',
394
- sg_ingress_db_port: PortNumber,
395
- sg_ingress_rules: [
394
+ SGDBAccessRules: [
395
+ {
396
+ "database": "sg-088a1b9d4918effb3", // Security groups that provide access to an RDS database
397
+ "port": 3306, // Can be multiple or none at all
398
+ "description": "MySQL access from jumpbox"
399
+ },
400
+ ],
401
+ SGIngressRules: [
396
402
  {
397
- "ipv4": "10.32.0.0/11",
398
- "port": 22,
403
+ "ipv4": "10.32.0.0/11", // Additional access rules to allow connection to the server
404
+ "port": 22, // Can be multiple or none at all
399
405
  "description": "SSH access from campus"
400
406
  },
401
407
  ],
@@ -1,6 +1,12 @@
1
- import { aws_ec2, Stack, StackProps } from 'aws-cdk-lib';
1
+ import { Stack, StackProps } from 'aws-cdk-lib';
2
+ import { InstanceClass, InstanceSize } from 'aws-cdk-lib/aws-ec2';
2
3
  import { Construct } from 'constructs';
3
- export interface IIngress_Rule {
4
+ export interface AccessRule {
5
+ readonly database: string;
6
+ readonly port: number;
7
+ readonly description: string;
8
+ }
9
+ export interface IIngressRule {
4
10
  readonly ipv4: string;
5
11
  readonly port: number;
6
12
  readonly description: string;
@@ -8,17 +14,16 @@ export interface IIngress_Rule {
8
14
  export interface ServiceStackProps extends StackProps {
9
15
  readonly amiId: string;
10
16
  readonly availabilityZones: string[];
11
- readonly instanceClass: aws_ec2.InstanceClass;
12
- readonly instanceSize: aws_ec2.InstanceSize;
17
+ readonly backup?: string;
18
+ readonly instanceClass: InstanceClass;
19
+ readonly instanceSize: InstanceSize;
13
20
  readonly instanceName: string;
14
21
  readonly keyName: string;
15
- readonly privateIpAddress: string;
16
22
  readonly publicSubnetIds: string[];
17
23
  readonly volumeSize: number;
18
24
  readonly vpcId: string;
19
- readonly sg_ingress_rules: IIngress_Rule[];
20
- readonly sg_ingress_db: string;
21
- readonly sg_ingress_db_port: number;
25
+ readonly SGIngressRules?: IIngressRule[];
26
+ readonly SGDBAccessRules?: AccessRule[];
22
27
  readonly domainName: string;
23
28
  readonly privateDomainName: string;
24
29
  readonly CnameList: string[];
@@ -4,11 +4,24 @@ exports.EC2withDatabase = void 0;
4
4
  const aws_cdk_lib_1 = require("aws-cdk-lib");
5
5
  const aws_cdk_lib_2 = require("aws-cdk-lib");
6
6
  const ssm = require("aws-cdk-lib/aws-ssm");
7
+ const aws_kms_1 = require("aws-cdk-lib/aws-kms");
7
8
  const aws_route53_1 = require("aws-cdk-lib/aws-route53");
8
9
  class EC2withDatabase extends aws_cdk_lib_1.Stack {
9
10
  constructor(scope, id, props) {
11
+ var _a, _b, _c;
10
12
  super(scope, id, props);
11
- // Determine the VPC from the availability zones and subnets passed in
13
+ /*
14
+ * Security Group setup and usage are optional parameters
15
+ * SGDBAccessRules = RDS database Access group rules (can be multiple)
16
+ * SGIngressRules = Ingress rules for additional access (can be multiple)
17
+ */
18
+ props = {
19
+ ...props,
20
+ backup: (_a = props.backup) !== null && _a !== void 0 ? _a : 'False',
21
+ SGDBAccessRules: (_b = props.SGDBAccessRules) !== null && _b !== void 0 ? _b : [],
22
+ SGIngressRules: (_c = props.SGIngressRules) !== null && _c !== void 0 ? _c : [],
23
+ };
24
+ // Determine the VPC from the availability zone and subnet passed in. These are expected to align within the VPC
12
25
  const vpc = aws_cdk_lib_1.aws_ec2.Vpc.fromVpcAttributes(this, 'Vpc', {
13
26
  vpcId: props.vpcId,
14
27
  availabilityZones: props.availabilityZones,
@@ -18,23 +31,39 @@ class EC2withDatabase extends aws_cdk_lib_1.Stack {
18
31
  const linux_build = aws_cdk_lib_1.aws_ec2.MachineImage.genericLinux({
19
32
  'us-east-1': props.amiId,
20
33
  });
21
- // Need to look up the role name and then get the ARN value
34
+ // Need to look up the basic role name and then get the ARN value
22
35
  const basic_role = ssm.StringParameter.valueFromLookup(this, '/esu/ec2/basic_role_name');
23
36
  const roleName = `arn:aws:iam::${this.account}:${basic_role}`;
24
37
  const ec2role = aws_cdk_lib_1.aws_iam.Role.fromRoleArn(this, 'Role', roleName);
25
- // Create a security group for this specific server using rules passed in
38
+ /*
39
+ * Create a security group for this specific server and then add the SGIngressRules passed in
40
+ */
26
41
  const ec2SecurityGroup = new aws_cdk_lib_1.aws_ec2.SecurityGroup(this, props.instanceName, {
27
42
  description: 'Security Group for $(props.instanceName)',
28
43
  allowAllOutbound: true,
29
44
  vpc: vpc,
30
45
  });
31
- // Add connections from the new security group to the RDS which will be used by this server
32
- const db_connect_SG = aws_cdk_lib_1.aws_ec2.SecurityGroup.fromSecurityGroupId(this, 'SG', props.sg_ingress_db);
33
- ec2SecurityGroup.connections.allowFrom(db_connect_SG, aws_cdk_lib_1.aws_ec2.Port.tcp(props.sg_ingress_db_port), 'Database SG Access');
34
- ec2SecurityGroup.connections.allowTo(db_connect_SG, aws_cdk_lib_1.aws_ec2.Port.tcp(props.sg_ingress_db_port), 'Database SG Access');
35
- props.sg_ingress_rules.forEach((rule) => {
36
- ec2SecurityGroup.addIngressRule(aws_cdk_lib_1.aws_ec2.Peer.ipv4(rule.ipv4), aws_cdk_lib_1.aws_ec2.Port.tcp(rule.port), rule.description);
46
+ if (props.SGIngressRules) {
47
+ props.SGIngressRules.forEach((rule) => {
48
+ ec2SecurityGroup.addIngressRule(aws_cdk_lib_1.aws_ec2.Peer.ipv4(rule.ipv4), aws_cdk_lib_1.aws_ec2.Port.tcp(rule.port), rule.description);
49
+ });
50
+ }
51
+ // Create a key to allow volume and instance to be backed up
52
+ const backupKey = new aws_kms_1.Key(this, 'BackupKey', {
53
+ enableKeyRotation: true,
54
+ alias: props.instanceName,
37
55
  });
56
+ /*
57
+ * Add connections from the new security group to the RDS which will be used by this server using
58
+ * SGDBAccessRules passed in
59
+ */
60
+ if (props.SGDBAccessRules) {
61
+ props.SGDBAccessRules.forEach((rule) => {
62
+ const db_connect_SG = aws_cdk_lib_1.aws_ec2.SecurityGroup.fromSecurityGroupId(this, `SGAccess${rule.database}`, rule.database);
63
+ ec2SecurityGroup.connections.allowFrom(db_connect_SG, aws_cdk_lib_1.aws_ec2.Port.tcp(rule.port), rule.description);
64
+ ec2SecurityGroup.connections.allowTo(db_connect_SG, aws_cdk_lib_1.aws_ec2.Port.tcp(rule.port), rule.description);
65
+ });
66
+ }
38
67
  // With all of the information gathered, as well as values passed in, create the server
39
68
  const ec2_server = new aws_cdk_lib_1.aws_ec2.Instance(this, 'ec2-server', {
40
69
  blockDevices: [{
@@ -43,6 +72,7 @@ class EC2withDatabase extends aws_cdk_lib_1.Stack {
43
72
  ebsDevice: {
44
73
  volumeSize: props.volumeSize,
45
74
  encrypted: true,
75
+ kmsKey: backupKey,
46
76
  },
47
77
  },
48
78
  }],
@@ -50,24 +80,33 @@ class EC2withDatabase extends aws_cdk_lib_1.Stack {
50
80
  role: ec2role,
51
81
  securityGroup: ec2SecurityGroup,
52
82
  machineImage: linux_build,
53
- // keyName: props.keyName, // deprecated, replaced with keyPair below.
83
+ propagateTagsToVolumeOnCreation: true,
54
84
  keyPair: aws_cdk_lib_1.aws_ec2.KeyPair.fromKeyPairName(this, 'KeyPair', props.keyName),
55
85
  instanceType: aws_cdk_lib_1.aws_ec2.InstanceType.of(props.instanceClass, props.instanceSize),
56
86
  instanceName: props.instanceName,
57
- privateIpAddress: props.privateIpAddress,
58
87
  });
59
88
  // Add an Elastic IP Address to the server
60
89
  const IPAddr = new aws_cdk_lib_1.aws_ec2.CfnEIP(this, 'Ip', {
61
90
  instanceId: ec2_server.instanceId,
62
91
  });
63
- // Then tag the EIP
92
+ // Tag the server and the EIP with the instance name and FQDN
93
+ const FQDN = `${props.instanceName}.${props.domainName}`;
94
+ const schedule = '*'; // Initially the schedule is always up
95
+ const service = props.instanceName; // Initially the service is the instance name
96
+ // Then tag the EIP and the server
64
97
  aws_cdk_lib_1.Tags.of(IPAddr).add('Name', props.instanceName);
98
+ aws_cdk_lib_1.Tags.of(ec2_server).add('FQDN', FQDN);
99
+ aws_cdk_lib_1.Tags.of(ec2_server).add('Schedule', schedule);
100
+ aws_cdk_lib_1.Tags.of(ec2_server).add('Service', service);
101
+ if (props.backup) {
102
+ aws_cdk_lib_1.Tags.of(ec2_server).add('Backup', props.backup);
103
+ }
65
104
  // Create DNS for libnd.nd.edu connection
66
105
  const privateZoneId = ssm.StringParameter.fromStringParameterAttributes(this, 'PrivateZoneId', {
67
106
  parameterName: `/all/dns/${props.keyName}/private/zoneId`,
68
107
  }).stringValue;
69
108
  new aws_route53_1.ARecord(this, 'PrivateARec', {
70
- target: aws_route53_1.RecordTarget.fromIpAddresses(props.privateIpAddress),
109
+ target: aws_route53_1.RecordTarget.fromIpAddresses(ec2_server.instancePrivateIp),
71
110
  recordName: props.instanceName,
72
111
  zone: aws_route53_1.HostedZone.fromHostedZoneAttributes(this, 'PrivateHostedZone', {
73
112
  hostedZoneId: privateZoneId,
@@ -109,7 +148,7 @@ class EC2withDatabase extends aws_cdk_lib_1.Stack {
109
148
  parameterName: `/all/dns/${props.domainName}/${direction}/zoneId`,
110
149
  }).stringValue;
111
150
  new aws_route53_1.ARecord(this, `${direction}ServiceARec`, {
112
- target: aws_route53_1.RecordTarget.fromIpAddresses(props.privateIpAddress),
151
+ target: aws_route53_1.RecordTarget.fromIpAddresses(ec2_server.instancePrivateIp),
113
152
  recordName: props.instanceName,
114
153
  zone: aws_route53_1.HostedZone.fromHostedZoneAttributes(this, `${direction}ImportedHostedZone`, {
115
154
  hostedZoneId: hostedZoneId,
@@ -120,7 +159,7 @@ class EC2withDatabase extends aws_cdk_lib_1.Stack {
120
159
  if (props.CnameList.length != 0) {
121
160
  props.CnameList.forEach(cname => {
122
161
  new aws_route53_1.ARecord(this, `${direction}${cname}`, {
123
- target: aws_route53_1.RecordTarget.fromIpAddresses(props.privateIpAddress),
162
+ target: aws_route53_1.RecordTarget.fromIpAddresses(ec2_server.instancePrivateIp),
124
163
  recordName: cname,
125
164
  zone: aws_route53_1.HostedZone.fromHostedZoneAttributes(this, `${direction}${cname}HostedZone`, {
126
165
  hostedZoneId: hostedZoneId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ndlib/ndlib-cdk2",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "description": "Reusable CDK2 modules used within Hesburgh Libraries of Notre Dame",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",