@dga-itc/aws-cdk-constructs 1.0.0
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 +219 -0
- package/dist/aws-cdk/constructs/acm.d.ts +28 -0
- package/dist/aws-cdk/constructs/acm.js +239 -0
- package/dist/aws-cdk/constructs/alb.d.ts +28 -0
- package/dist/aws-cdk/constructs/alb.js +304 -0
- package/dist/aws-cdk/constructs/bastion.d.ts +46 -0
- package/dist/aws-cdk/constructs/bastion.js +332 -0
- package/dist/aws-cdk/constructs/cloudfront.d.ts +45 -0
- package/dist/aws-cdk/constructs/cloudfront.js +261 -0
- package/dist/aws-cdk/constructs/ecr.d.ts +17 -0
- package/dist/aws-cdk/constructs/ecr.js +143 -0
- package/dist/aws-cdk/constructs/ecs-cluster.d.ts +21 -0
- package/dist/aws-cdk/constructs/ecs-cluster.js +124 -0
- package/dist/aws-cdk/constructs/ecs-service.d.ts +72 -0
- package/dist/aws-cdk/constructs/ecs-service.js +682 -0
- package/dist/aws-cdk/constructs/efs.d.ts +31 -0
- package/dist/aws-cdk/constructs/efs.js +241 -0
- package/dist/aws-cdk/constructs/elasticache.d.ts +35 -0
- package/dist/aws-cdk/constructs/elasticache.js +210 -0
- package/dist/aws-cdk/constructs/nacl.d.ts +37 -0
- package/dist/aws-cdk/constructs/nacl.js +88 -0
- package/dist/aws-cdk/constructs/nlb.d.ts +39 -0
- package/dist/aws-cdk/constructs/nlb.js +276 -0
- package/dist/aws-cdk/constructs/rds.d.ts +40 -0
- package/dist/aws-cdk/constructs/rds.js +320 -0
- package/dist/aws-cdk/constructs/self-signed-cert.d.ts +83 -0
- package/dist/aws-cdk/constructs/self-signed-cert.js +215 -0
- package/dist/aws-cdk/constructs/sqs.d.ts +30 -0
- package/dist/aws-cdk/constructs/sqs.js +268 -0
- package/dist/aws-cdk/constructs/vpc.d.ts +30 -0
- package/dist/aws-cdk/constructs/vpc.js +423 -0
- package/dist/aws-cdk/constructs/waf.d.ts +37 -0
- package/dist/aws-cdk/constructs/waf.js +350 -0
- package/dist/aws-cdk/interfaces/account-config.d.ts +18 -0
- package/dist/aws-cdk/interfaces/account-config.js +2 -0
- package/dist/aws-cdk/interfaces/acm-config.d.ts +94 -0
- package/dist/aws-cdk/interfaces/acm-config.js +14 -0
- package/dist/aws-cdk/interfaces/alb-config.d.ts +72 -0
- package/dist/aws-cdk/interfaces/alb-config.js +2 -0
- package/dist/aws-cdk/interfaces/bastion-config.d.ts +77 -0
- package/dist/aws-cdk/interfaces/bastion-config.js +10 -0
- package/dist/aws-cdk/interfaces/cloudfront-config.d.ts +154 -0
- package/dist/aws-cdk/interfaces/cloudfront-config.js +15 -0
- package/dist/aws-cdk/interfaces/ecr-config.d.ts +40 -0
- package/dist/aws-cdk/interfaces/ecr-config.js +2 -0
- package/dist/aws-cdk/interfaces/ecs-cluster-config.d.ts +30 -0
- package/dist/aws-cdk/interfaces/ecs-cluster-config.js +2 -0
- package/dist/aws-cdk/interfaces/ecs-service-config.d.ts +237 -0
- package/dist/aws-cdk/interfaces/ecs-service-config.js +2 -0
- package/dist/aws-cdk/interfaces/efs-config.d.ts +56 -0
- package/dist/aws-cdk/interfaces/efs-config.js +7 -0
- package/dist/aws-cdk/interfaces/elasticache-config.d.ts +56 -0
- package/dist/aws-cdk/interfaces/elasticache-config.js +7 -0
- package/dist/aws-cdk/interfaces/nacl-config.d.ts +1 -0
- package/dist/aws-cdk/interfaces/nacl-config.js +3 -0
- package/dist/aws-cdk/interfaces/nlb-config.d.ts +69 -0
- package/dist/aws-cdk/interfaces/nlb-config.js +2 -0
- package/dist/aws-cdk/interfaces/rds-config.d.ts +84 -0
- package/dist/aws-cdk/interfaces/rds-config.js +7 -0
- package/dist/aws-cdk/interfaces/sqs-config.d.ts +145 -0
- package/dist/aws-cdk/interfaces/sqs-config.js +12 -0
- package/dist/aws-cdk/interfaces/tag-config.d.ts +18 -0
- package/dist/aws-cdk/interfaces/tag-config.js +2 -0
- package/dist/aws-cdk/interfaces/vpc-config.d.ts +72 -0
- package/dist/aws-cdk/interfaces/vpc-config.js +2 -0
- package/dist/aws-cdk/interfaces/waf-config.d.ts +180 -0
- package/dist/aws-cdk/interfaces/waf-config.js +2 -0
- package/dist/aws-cdk/utils/priority-tracker.d.ts +60 -0
- package/dist/aws-cdk/utils/priority-tracker.js +131 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.js +55 -0
- package/dist/terraform-cdk/constructs/alb-listener-rule.d.ts +33 -0
- package/dist/terraform-cdk/constructs/alb-listener-rule.js +81 -0
- package/dist/terraform-cdk/constructs/ecs-service.d.ts +29 -0
- package/dist/terraform-cdk/constructs/ecs-service.js +238 -0
- package/dist/terraform-cdk/interfaces/ecs-service-config.d.ts +53 -0
- package/dist/terraform-cdk/interfaces/ecs-service-config.js +25 -0
- package/dist/terraform-cdk/interfaces/infrastructure-refs.d.ts +16 -0
- package/dist/terraform-cdk/interfaces/infrastructure-refs.js +8 -0
- package/dist/terraform-cdk/utils/priority-tracker.d.ts +60 -0
- package/dist/terraform-cdk/utils/priority-tracker.js +131 -0
- package/package.json +46 -0
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.VpcConstruct = void 0;
|
|
37
|
+
const constructs_1 = require("constructs");
|
|
38
|
+
const cdk = __importStar(require("aws-cdk-lib"));
|
|
39
|
+
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
40
|
+
const ec2 = __importStar(require("aws-cdk-lib/aws-ec2"));
|
|
41
|
+
const logs = __importStar(require("aws-cdk-lib/aws-logs"));
|
|
42
|
+
const iam = __importStar(require("aws-cdk-lib/aws-iam"));
|
|
43
|
+
const nacl_1 = require("./nacl");
|
|
44
|
+
class VpcConstruct extends constructs_1.Construct {
|
|
45
|
+
constructor(scope, id, props) {
|
|
46
|
+
super(scope, id);
|
|
47
|
+
this.subnets = new Map();
|
|
48
|
+
this.subnetsByName = new Map();
|
|
49
|
+
this.routeTables = new Map();
|
|
50
|
+
/** Map: subnetKey (name-az) → routeTableId (CfnRouteTable.ref) */
|
|
51
|
+
this.subnetRouteTableMap = new Map();
|
|
52
|
+
this.natGateways = [];
|
|
53
|
+
const { config } = props;
|
|
54
|
+
// Resolve region: ใช้ config.region ถ้ามี, ไม่งั้น fallback ไป Stack region
|
|
55
|
+
const resolvedRegion = config.region ?? cdk.Stack.of(this).region;
|
|
56
|
+
// Determine removal policy (default: retain for prod, destroy for others)
|
|
57
|
+
this.removalPolicy = config.removalPolicy === 'retain'
|
|
58
|
+
? aws_cdk_lib_1.RemovalPolicy.RETAIN
|
|
59
|
+
: config.removalPolicy === 'destroy'
|
|
60
|
+
? aws_cdk_lib_1.RemovalPolicy.DESTROY
|
|
61
|
+
: config.type === 'prod'
|
|
62
|
+
? aws_cdk_lib_1.RemovalPolicy.RETAIN
|
|
63
|
+
: aws_cdk_lib_1.RemovalPolicy.DESTROY;
|
|
64
|
+
// 1. Create VPC
|
|
65
|
+
this.vpc = new ec2.CfnVPC(this, 'Vpc', {
|
|
66
|
+
cidrBlock: config.cidr,
|
|
67
|
+
enableDnsHostnames: config.enableDnsHostnames ?? true,
|
|
68
|
+
enableDnsSupport: config.enableDnsSupport ?? true,
|
|
69
|
+
tags: [{ key: 'Name', value: `${config.name}-${config.type}-vpc` }],
|
|
70
|
+
});
|
|
71
|
+
this.vpc.applyRemovalPolicy(this.removalPolicy);
|
|
72
|
+
// 1.1 Add Secondary CIDRs
|
|
73
|
+
const allCidrs = [config.cidr, ...(config.secondaryCidrs || [])];
|
|
74
|
+
const secondaryCidrBlocks = [];
|
|
75
|
+
if (config.secondaryCidrs) {
|
|
76
|
+
config.secondaryCidrs.forEach((cidr, index) => {
|
|
77
|
+
const cidrBlock = new ec2.CfnVPCCidrBlock(this, `SecondaryCidr-${index}`, {
|
|
78
|
+
vpcId: this.vpc.ref,
|
|
79
|
+
cidrBlock: cidr,
|
|
80
|
+
});
|
|
81
|
+
cidrBlock.applyRemovalPolicy(this.removalPolicy);
|
|
82
|
+
secondaryCidrBlocks.push(cidrBlock);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
// Helper: check if subnet CIDR is in secondary CIDR range
|
|
86
|
+
const isInSecondaryCidr = (subnetCidr) => {
|
|
87
|
+
if (!config.secondaryCidrs)
|
|
88
|
+
return false;
|
|
89
|
+
const subnetIp = subnetCidr.split('/')[0].split('.').map(Number);
|
|
90
|
+
return config.secondaryCidrs.some(cidr => {
|
|
91
|
+
const [cidrIp, cidrMask] = cidr.split('/');
|
|
92
|
+
const cidrParts = cidrIp.split('.').map(Number);
|
|
93
|
+
const mask = parseInt(cidrMask);
|
|
94
|
+
const maskBits = ~((1 << (32 - mask)) - 1) >>> 0;
|
|
95
|
+
const subnetNum = (subnetIp[0] << 24) + (subnetIp[1] << 16) + (subnetIp[2] << 8) + subnetIp[3];
|
|
96
|
+
const cidrNum = (cidrParts[0] << 24) + (cidrParts[1] << 16) + (cidrParts[2] << 8) + cidrParts[3];
|
|
97
|
+
return (subnetNum & maskBits) === (cidrNum & maskBits);
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
// 2. Create Internet Gateway (ถ้ามี public subnet)
|
|
101
|
+
const hasPublicSubnet = config.subnets.some(s => s.type === 'public');
|
|
102
|
+
if (hasPublicSubnet) {
|
|
103
|
+
this.igw = new ec2.CfnInternetGateway(this, 'Igw', {
|
|
104
|
+
tags: [{ key: 'Name', value: `${config.name}-${config.type}-igw` }],
|
|
105
|
+
});
|
|
106
|
+
this.igw.applyRemovalPolicy(this.removalPolicy);
|
|
107
|
+
const igwAttachment = new ec2.CfnVPCGatewayAttachment(this, 'IgwAttachment', {
|
|
108
|
+
vpcId: this.vpc.ref,
|
|
109
|
+
internetGatewayId: this.igw.ref,
|
|
110
|
+
});
|
|
111
|
+
igwAttachment.applyRemovalPolicy(this.removalPolicy);
|
|
112
|
+
}
|
|
113
|
+
// 3. Create NAT Gateways ก่อน (เพื่อให้ใช้ใน custom routes ได้)
|
|
114
|
+
// เก็บ NAT subnet configs ไว้สำหรับสร้าง route table ทีหลัง
|
|
115
|
+
const natSubnetConfigs = [];
|
|
116
|
+
if (config.natGateways) {
|
|
117
|
+
config.natGateways.forEach((natConfig, index) => {
|
|
118
|
+
// หา public subnet ที่ match (ถ้าระบุ az จะหาตาม az)
|
|
119
|
+
const publicSubnetConfig = config.subnets.find(s => s.type === 'public' &&
|
|
120
|
+
s.name === natConfig.subnetName &&
|
|
121
|
+
(natConfig.az ? s.az === natConfig.az : true));
|
|
122
|
+
if (publicSubnetConfig) {
|
|
123
|
+
const eip = new ec2.CfnEIP(this, `NatEip-${index}`, {
|
|
124
|
+
domain: 'vpc',
|
|
125
|
+
tags: [{ key: 'Name', value: `${config.name}-${config.type}-nat-eip-${publicSubnetConfig.az}` }],
|
|
126
|
+
});
|
|
127
|
+
eip.applyRemovalPolicy(this.removalPolicy);
|
|
128
|
+
// สร้าง subnet สำหรับ NAT ก่อน
|
|
129
|
+
const subnetKey = `${publicSubnetConfig.name}-${publicSubnetConfig.az}`;
|
|
130
|
+
// ตรวจสอบว่า subnet นี้สร้างไปแล้วหรือยัง
|
|
131
|
+
let natSubnet = this.subnets.get(subnetKey);
|
|
132
|
+
if (!natSubnet) {
|
|
133
|
+
natSubnet = new ec2.CfnSubnet(this, `NatSubnet-${publicSubnetConfig.az}`, {
|
|
134
|
+
vpcId: this.vpc.ref,
|
|
135
|
+
cidrBlock: publicSubnetConfig.cidr,
|
|
136
|
+
availabilityZone: `${resolvedRegion}${publicSubnetConfig.az}`,
|
|
137
|
+
mapPublicIpOnLaunch: true,
|
|
138
|
+
tags: [{ key: 'Name', value: `${config.name}-${config.type}-${publicSubnetConfig.name}-${publicSubnetConfig.az}` }],
|
|
139
|
+
});
|
|
140
|
+
natSubnet.applyRemovalPolicy(this.removalPolicy);
|
|
141
|
+
// Track NAT subnet in maps for exports
|
|
142
|
+
this.subnets.set(subnetKey, natSubnet);
|
|
143
|
+
if (!this.subnetsByName.has(publicSubnetConfig.name)) {
|
|
144
|
+
this.subnetsByName.set(publicSubnetConfig.name, []);
|
|
145
|
+
}
|
|
146
|
+
this.subnetsByName.get(publicSubnetConfig.name).push(natSubnet);
|
|
147
|
+
// เก็บ config ไว้สำหรับสร้าง route table
|
|
148
|
+
natSubnetConfigs.push(publicSubnetConfig);
|
|
149
|
+
}
|
|
150
|
+
const nat = new ec2.CfnNatGateway(this, `Nat-${publicSubnetConfig.az}`, {
|
|
151
|
+
subnetId: natSubnet.ref,
|
|
152
|
+
allocationId: eip.attrAllocationId,
|
|
153
|
+
tags: [{ key: 'Name', value: `${config.name}-${config.type}-nat-${publicSubnetConfig.az}` }],
|
|
154
|
+
});
|
|
155
|
+
nat.applyRemovalPolicy(this.removalPolicy);
|
|
156
|
+
this.natGateways.push(nat);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
// 3.1 Create Route Tables for NAT subnets
|
|
161
|
+
natSubnetConfigs.forEach(subnetConfig => {
|
|
162
|
+
const subnetKey = `${subnetConfig.name}-${subnetConfig.az}`;
|
|
163
|
+
const natSubnet = this.subnets.get(subnetKey);
|
|
164
|
+
const routeTablePerAz = config.routeTablePerAz ?? true;
|
|
165
|
+
const rtKey = routeTablePerAz
|
|
166
|
+
? `${subnetConfig.name}-${subnetConfig.az}`
|
|
167
|
+
: subnetConfig.name;
|
|
168
|
+
let routeTable;
|
|
169
|
+
if (this.routeTables.has(rtKey)) {
|
|
170
|
+
routeTable = this.routeTables.get(rtKey);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
const rtName = routeTablePerAz
|
|
174
|
+
? `${config.name}-${config.type}-${subnetConfig.name}-${subnetConfig.az}-rt`
|
|
175
|
+
: `${config.name}-${config.type}-${subnetConfig.name}-rt`;
|
|
176
|
+
routeTable = new ec2.CfnRouteTable(this, `RT-${rtKey}`, {
|
|
177
|
+
vpcId: this.vpc.ref,
|
|
178
|
+
tags: [{ key: 'Name', value: rtName }],
|
|
179
|
+
});
|
|
180
|
+
routeTable.applyRemovalPolicy(this.removalPolicy);
|
|
181
|
+
this.routeTables.set(rtKey, routeTable);
|
|
182
|
+
// Add routes
|
|
183
|
+
this.createRoutesForRouteTable(config, subnetConfig, routeTable, rtKey);
|
|
184
|
+
}
|
|
185
|
+
const rtAssoc = new ec2.CfnSubnetRouteTableAssociation(this, `RTAssoc-${subnetKey}`, {
|
|
186
|
+
subnetId: natSubnet.ref,
|
|
187
|
+
routeTableId: routeTable.ref,
|
|
188
|
+
});
|
|
189
|
+
rtAssoc.applyRemovalPolicy(this.removalPolicy);
|
|
190
|
+
// Track subnet → routeTable mapping
|
|
191
|
+
this.subnetRouteTableMap.set(subnetKey, routeTable.ref);
|
|
192
|
+
});
|
|
193
|
+
// 4. Create Subnets และ Route Tables
|
|
194
|
+
const createdSubnets = new Set(); // track subnets ที่สร้างแล้ว (จาก NAT)
|
|
195
|
+
// Mark NAT subnets as created
|
|
196
|
+
if (config.natGateways) {
|
|
197
|
+
config.natGateways.forEach(natConfig => {
|
|
198
|
+
const publicSubnetConfig = config.subnets.find(s => s.type === 'public' &&
|
|
199
|
+
s.name === natConfig.subnetName &&
|
|
200
|
+
(natConfig.az ? s.az === natConfig.az : true));
|
|
201
|
+
if (publicSubnetConfig) {
|
|
202
|
+
createdSubnets.add(`${publicSubnetConfig.name}-${publicSubnetConfig.az}`);
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
config.subnets.forEach((subnetConfig, index) => {
|
|
207
|
+
const subnetKey = `${subnetConfig.name}-${subnetConfig.az}`;
|
|
208
|
+
// Skip ถ้าสร้างไปแล้ว (จาก NAT)
|
|
209
|
+
if (createdSubnets.has(subnetKey)) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const subnet = new ec2.CfnSubnet(this, `Subnet-${subnetKey}`, {
|
|
213
|
+
vpcId: this.vpc.ref,
|
|
214
|
+
cidrBlock: subnetConfig.cidr,
|
|
215
|
+
availabilityZone: `${resolvedRegion}${subnetConfig.az}`,
|
|
216
|
+
mapPublicIpOnLaunch: subnetConfig.type === 'public',
|
|
217
|
+
tags: [{ key: 'Name', value: `${config.name}-${config.type}-${subnetConfig.name}-${subnetConfig.az}` }],
|
|
218
|
+
});
|
|
219
|
+
subnet.applyRemovalPolicy(this.removalPolicy);
|
|
220
|
+
// Add dependency on secondary CIDR if subnet is in secondary range
|
|
221
|
+
if (isInSecondaryCidr(subnetConfig.cidr)) {
|
|
222
|
+
secondaryCidrBlocks.forEach(cidrBlock => {
|
|
223
|
+
subnet.addDependency(cidrBlock);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
this.subnets.set(subnetKey, subnet);
|
|
227
|
+
// Group by name for NACLs and exports
|
|
228
|
+
if (!this.subnetsByName.has(subnetConfig.name)) {
|
|
229
|
+
this.subnetsByName.set(subnetConfig.name, []);
|
|
230
|
+
}
|
|
231
|
+
this.subnetsByName.get(subnetConfig.name).push(subnet);
|
|
232
|
+
// Create Route Table
|
|
233
|
+
const routeTablePerAz = config.routeTablePerAz ?? true;
|
|
234
|
+
const rtKey = routeTablePerAz
|
|
235
|
+
? `${subnetConfig.name}-${subnetConfig.az}` // แยกตาม AZ
|
|
236
|
+
: subnetConfig.name; // ใช้ร่วมกันตาม name
|
|
237
|
+
let routeTable;
|
|
238
|
+
if (this.routeTables.has(rtKey)) {
|
|
239
|
+
// ใช้ route table ที่มีอยู่แล้ว
|
|
240
|
+
routeTable = this.routeTables.get(rtKey);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
// สร้าง route table ใหม่
|
|
244
|
+
const rtName = routeTablePerAz
|
|
245
|
+
? `${config.name}-${config.type}-${subnetConfig.name}-${subnetConfig.az}-rt`
|
|
246
|
+
: `${config.name}-${config.type}-${subnetConfig.name}-rt`;
|
|
247
|
+
routeTable = new ec2.CfnRouteTable(this, `RT-${rtKey}`, {
|
|
248
|
+
vpcId: this.vpc.ref,
|
|
249
|
+
tags: [{ key: 'Name', value: rtName }],
|
|
250
|
+
});
|
|
251
|
+
routeTable.applyRemovalPolicy(this.removalPolicy);
|
|
252
|
+
this.routeTables.set(rtKey, routeTable);
|
|
253
|
+
// Add routes from routeTables config or use default
|
|
254
|
+
this.createRoutesForRouteTable(config, subnetConfig, routeTable, rtKey);
|
|
255
|
+
}
|
|
256
|
+
const rtAssoc = new ec2.CfnSubnetRouteTableAssociation(this, `RTAssoc-${subnetKey}`, {
|
|
257
|
+
subnetId: subnet.ref,
|
|
258
|
+
routeTableId: routeTable.ref,
|
|
259
|
+
});
|
|
260
|
+
rtAssoc.applyRemovalPolicy(this.removalPolicy);
|
|
261
|
+
// Track subnet → routeTable mapping
|
|
262
|
+
this.subnetRouteTableMap.set(subnetKey, routeTable.ref);
|
|
263
|
+
});
|
|
264
|
+
// 5. Enable Flow Logs
|
|
265
|
+
if (config.enableFlowLogs) {
|
|
266
|
+
const logGroup = new logs.LogGroup(this, 'FlowLogGroup', {
|
|
267
|
+
logGroupName: `/vpc/${config.name}-${config.type}/flow-logs`,
|
|
268
|
+
retention: logs.RetentionDays.ONE_MONTH,
|
|
269
|
+
removalPolicy: this.removalPolicy,
|
|
270
|
+
});
|
|
271
|
+
const flowLogRole = new iam.Role(this, 'FlowLogRole', {
|
|
272
|
+
assumedBy: new iam.ServicePrincipal('vpc-flow-logs.amazonaws.com'),
|
|
273
|
+
});
|
|
274
|
+
flowLogRole.applyRemovalPolicy(this.removalPolicy);
|
|
275
|
+
logGroup.grantWrite(flowLogRole);
|
|
276
|
+
const flowLog = new ec2.CfnFlowLog(this, 'FlowLog', {
|
|
277
|
+
resourceId: this.vpc.ref,
|
|
278
|
+
resourceType: 'VPC',
|
|
279
|
+
trafficType: 'ALL',
|
|
280
|
+
logDestinationType: 'cloud-watch-logs',
|
|
281
|
+
logGroupName: logGroup.logGroupName,
|
|
282
|
+
deliverLogsPermissionArn: flowLogRole.roleArn,
|
|
283
|
+
});
|
|
284
|
+
flowLog.applyRemovalPolicy(this.removalPolicy);
|
|
285
|
+
}
|
|
286
|
+
// 6. Create NACLs (inline mode)
|
|
287
|
+
if (config.nacls && config.nacls.length > 0) {
|
|
288
|
+
new nacl_1.NaclConstruct(this, 'Nacl', {
|
|
289
|
+
vpcId: this.vpc.ref,
|
|
290
|
+
namePrefix: `${config.name}-${config.type}`,
|
|
291
|
+
nacls: config.nacls,
|
|
292
|
+
subnets: this.subnetsByName,
|
|
293
|
+
tags: config.tags,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
createRoutesForRouteTable(config, subnetConfig, routeTable, rtKey) {
|
|
298
|
+
const routeTablePerAz = config.routeTablePerAz ?? true;
|
|
299
|
+
// หา custom route table config ที่ match
|
|
300
|
+
let rtConfig;
|
|
301
|
+
if (config.routeTables) {
|
|
302
|
+
if (routeTablePerAz) {
|
|
303
|
+
// หา config ที่ match ทั้ง name และ az
|
|
304
|
+
rtConfig = config.routeTables.find(rt => rt.name === subnetConfig.name && rt.az === subnetConfig.az);
|
|
305
|
+
// ถ้าไม่เจอ ลองหาที่ match แค่ name (ไม่ระบุ az = ใช้กับทุก az)
|
|
306
|
+
if (!rtConfig) {
|
|
307
|
+
rtConfig = config.routeTables.find(rt => rt.name === subnetConfig.name && !rt.az);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
// หา config ที่ match name
|
|
312
|
+
rtConfig = config.routeTables.find(rt => rt.name === subnetConfig.name);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (rtConfig) {
|
|
316
|
+
// ใช้ custom routes
|
|
317
|
+
rtConfig.routes.forEach((route, routeIndex) => {
|
|
318
|
+
this.createRoute(routeTable, route.destinationCidr, route.target, `${rtKey}-${routeIndex}`);
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
// ใช้ default routes ตาม subnet type
|
|
323
|
+
this.createDefaultRoutes(subnetConfig, routeTable, rtKey);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
createDefaultRoutes(subnetConfig, routeTable, rtKey) {
|
|
327
|
+
switch (subnetConfig.type) {
|
|
328
|
+
case 'public':
|
|
329
|
+
if (this.igw) {
|
|
330
|
+
new ec2.CfnRoute(this, `Route-Default-${rtKey}`, {
|
|
331
|
+
routeTableId: routeTable.ref,
|
|
332
|
+
destinationCidrBlock: '0.0.0.0/0',
|
|
333
|
+
gatewayId: this.igw.ref,
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
break;
|
|
337
|
+
case 'private':
|
|
338
|
+
if (this.natGateways.length > 0) {
|
|
339
|
+
new ec2.CfnRoute(this, `Route-Default-${rtKey}`, {
|
|
340
|
+
routeTableId: routeTable.ref,
|
|
341
|
+
destinationCidrBlock: '0.0.0.0/0',
|
|
342
|
+
natGatewayId: this.natGateways[0].ref,
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
break;
|
|
346
|
+
// isolated ไม่มี default route
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
createRoute(routeTable, destinationCidr, target, routeId) {
|
|
350
|
+
const routeProps = {
|
|
351
|
+
routeTableId: routeTable.ref,
|
|
352
|
+
destinationCidrBlock: destinationCidr,
|
|
353
|
+
};
|
|
354
|
+
switch (target.type) {
|
|
355
|
+
case 'igw':
|
|
356
|
+
if (this.igw) {
|
|
357
|
+
new ec2.CfnRoute(this, `Route-${routeId}`, {
|
|
358
|
+
...routeProps,
|
|
359
|
+
gatewayId: this.igw.ref,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
break;
|
|
363
|
+
case 'nat':
|
|
364
|
+
if (this.natGateways[target.natIndex]) {
|
|
365
|
+
new ec2.CfnRoute(this, `Route-${routeId}`, {
|
|
366
|
+
...routeProps,
|
|
367
|
+
natGatewayId: this.natGateways[target.natIndex].ref,
|
|
368
|
+
});
|
|
369
|
+
}
|
|
370
|
+
break;
|
|
371
|
+
case 'vpcPeering':
|
|
372
|
+
new ec2.CfnRoute(this, `Route-${routeId}`, {
|
|
373
|
+
...routeProps,
|
|
374
|
+
vpcPeeringConnectionId: target.peeringId,
|
|
375
|
+
});
|
|
376
|
+
break;
|
|
377
|
+
case 'transitGateway':
|
|
378
|
+
new ec2.CfnRoute(this, `Route-${routeId}`, {
|
|
379
|
+
...routeProps,
|
|
380
|
+
transitGatewayId: target.tgwId,
|
|
381
|
+
});
|
|
382
|
+
break;
|
|
383
|
+
case 'vpcEndpoint':
|
|
384
|
+
new ec2.CfnRoute(this, `Route-${routeId}`, {
|
|
385
|
+
...routeProps,
|
|
386
|
+
vpcEndpointId: target.endpointId,
|
|
387
|
+
});
|
|
388
|
+
break;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Get routeTableId for a subnet by name and az
|
|
393
|
+
* ลองหา key "name-az" ก่อน, ถ้าไม่เจอ fallback ไป "name" (routeTablePerAz: false)
|
|
394
|
+
*/
|
|
395
|
+
getRouteTableId(subnetName, az) {
|
|
396
|
+
return this.subnetRouteTableMap.get(`${subnetName}-${az}`)
|
|
397
|
+
?? this.subnetRouteTableMap.get(subnetName);
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Export VPC and subnet IDs for cross-stack references (e.g., Network Security Stack)
|
|
401
|
+
*/
|
|
402
|
+
exportForSecurityStack(stackName) {
|
|
403
|
+
const stack = cdk.Stack.of(this);
|
|
404
|
+
// Export VPC ID
|
|
405
|
+
new cdk.CfnOutput(stack, 'VpcIdExport', {
|
|
406
|
+
value: this.vpc.ref,
|
|
407
|
+
exportName: `${stackName}-VpcId`,
|
|
408
|
+
});
|
|
409
|
+
// Export subnet IDs grouped by name
|
|
410
|
+
this.subnetsByName.forEach((subnets, name) => {
|
|
411
|
+
const subnetIds = subnets.map(s => s.ref);
|
|
412
|
+
new cdk.CfnOutput(stack, `SubnetIds-${name}`, {
|
|
413
|
+
value: cdk.Fn.join(',', subnetIds),
|
|
414
|
+
exportName: `${stackName}-SubnetIds-${name}`,
|
|
415
|
+
});
|
|
416
|
+
new cdk.CfnOutput(stack, `SubnetCount-${name}`, {
|
|
417
|
+
value: subnets.length.toString(),
|
|
418
|
+
exportName: `${stackName}-SubnetCount-${name}`,
|
|
419
|
+
});
|
|
420
|
+
});
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
exports.VpcConstruct = VpcConstruct;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Construct } from 'constructs';
|
|
2
|
+
import * as wafv2 from 'aws-cdk-lib/aws-wafv2';
|
|
3
|
+
import * as logs from 'aws-cdk-lib/aws-logs';
|
|
4
|
+
import { WafConfig } from '../interfaces/waf-config';
|
|
5
|
+
export interface WafConstructProps {
|
|
6
|
+
config: WafConfig;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* WAF v2 Construct — สร้าง WebACL พร้อม AWS Managed Rule Sets
|
|
10
|
+
*
|
|
11
|
+
* Features:
|
|
12
|
+
* - AWS Managed Rule Groups (Common, SQLi, KnownBadInputs, etc.)
|
|
13
|
+
* - Rate Limiting per IP
|
|
14
|
+
* - Geo Blocking
|
|
15
|
+
* - IP Allow/Block Lists
|
|
16
|
+
* - CloudWatch Logging (filtered)
|
|
17
|
+
*
|
|
18
|
+
* Scopes:
|
|
19
|
+
* - CLOUDFRONT → ต้อง deploy ใน us-east-1, ใช้กับ CloudFront
|
|
20
|
+
* - REGIONAL → ใช้กับ ALB, API Gateway, AppSync
|
|
21
|
+
*/
|
|
22
|
+
export declare class WafConstruct extends Construct {
|
|
23
|
+
readonly webAcl: wafv2.CfnWebACL;
|
|
24
|
+
readonly webAclArn: string;
|
|
25
|
+
readonly logGroup?: logs.LogGroup;
|
|
26
|
+
private readonly removalPolicy;
|
|
27
|
+
constructor(scope: Construct, id: string, props: WafConstructProps);
|
|
28
|
+
private createIpSets;
|
|
29
|
+
private createIpSet;
|
|
30
|
+
private buildRules;
|
|
31
|
+
private buildManagedRuleGroup;
|
|
32
|
+
private resolveAction;
|
|
33
|
+
private createWebAcl;
|
|
34
|
+
private createLogging;
|
|
35
|
+
private createAssociations;
|
|
36
|
+
private createOutputs;
|
|
37
|
+
}
|