@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.
Files changed (82) hide show
  1. package/README.md +219 -0
  2. package/dist/aws-cdk/constructs/acm.d.ts +28 -0
  3. package/dist/aws-cdk/constructs/acm.js +239 -0
  4. package/dist/aws-cdk/constructs/alb.d.ts +28 -0
  5. package/dist/aws-cdk/constructs/alb.js +304 -0
  6. package/dist/aws-cdk/constructs/bastion.d.ts +46 -0
  7. package/dist/aws-cdk/constructs/bastion.js +332 -0
  8. package/dist/aws-cdk/constructs/cloudfront.d.ts +45 -0
  9. package/dist/aws-cdk/constructs/cloudfront.js +261 -0
  10. package/dist/aws-cdk/constructs/ecr.d.ts +17 -0
  11. package/dist/aws-cdk/constructs/ecr.js +143 -0
  12. package/dist/aws-cdk/constructs/ecs-cluster.d.ts +21 -0
  13. package/dist/aws-cdk/constructs/ecs-cluster.js +124 -0
  14. package/dist/aws-cdk/constructs/ecs-service.d.ts +72 -0
  15. package/dist/aws-cdk/constructs/ecs-service.js +682 -0
  16. package/dist/aws-cdk/constructs/efs.d.ts +31 -0
  17. package/dist/aws-cdk/constructs/efs.js +241 -0
  18. package/dist/aws-cdk/constructs/elasticache.d.ts +35 -0
  19. package/dist/aws-cdk/constructs/elasticache.js +210 -0
  20. package/dist/aws-cdk/constructs/nacl.d.ts +37 -0
  21. package/dist/aws-cdk/constructs/nacl.js +88 -0
  22. package/dist/aws-cdk/constructs/nlb.d.ts +39 -0
  23. package/dist/aws-cdk/constructs/nlb.js +276 -0
  24. package/dist/aws-cdk/constructs/rds.d.ts +40 -0
  25. package/dist/aws-cdk/constructs/rds.js +320 -0
  26. package/dist/aws-cdk/constructs/self-signed-cert.d.ts +83 -0
  27. package/dist/aws-cdk/constructs/self-signed-cert.js +215 -0
  28. package/dist/aws-cdk/constructs/sqs.d.ts +30 -0
  29. package/dist/aws-cdk/constructs/sqs.js +268 -0
  30. package/dist/aws-cdk/constructs/vpc.d.ts +30 -0
  31. package/dist/aws-cdk/constructs/vpc.js +423 -0
  32. package/dist/aws-cdk/constructs/waf.d.ts +37 -0
  33. package/dist/aws-cdk/constructs/waf.js +350 -0
  34. package/dist/aws-cdk/interfaces/account-config.d.ts +18 -0
  35. package/dist/aws-cdk/interfaces/account-config.js +2 -0
  36. package/dist/aws-cdk/interfaces/acm-config.d.ts +94 -0
  37. package/dist/aws-cdk/interfaces/acm-config.js +14 -0
  38. package/dist/aws-cdk/interfaces/alb-config.d.ts +72 -0
  39. package/dist/aws-cdk/interfaces/alb-config.js +2 -0
  40. package/dist/aws-cdk/interfaces/bastion-config.d.ts +77 -0
  41. package/dist/aws-cdk/interfaces/bastion-config.js +10 -0
  42. package/dist/aws-cdk/interfaces/cloudfront-config.d.ts +154 -0
  43. package/dist/aws-cdk/interfaces/cloudfront-config.js +15 -0
  44. package/dist/aws-cdk/interfaces/ecr-config.d.ts +40 -0
  45. package/dist/aws-cdk/interfaces/ecr-config.js +2 -0
  46. package/dist/aws-cdk/interfaces/ecs-cluster-config.d.ts +30 -0
  47. package/dist/aws-cdk/interfaces/ecs-cluster-config.js +2 -0
  48. package/dist/aws-cdk/interfaces/ecs-service-config.d.ts +237 -0
  49. package/dist/aws-cdk/interfaces/ecs-service-config.js +2 -0
  50. package/dist/aws-cdk/interfaces/efs-config.d.ts +56 -0
  51. package/dist/aws-cdk/interfaces/efs-config.js +7 -0
  52. package/dist/aws-cdk/interfaces/elasticache-config.d.ts +56 -0
  53. package/dist/aws-cdk/interfaces/elasticache-config.js +7 -0
  54. package/dist/aws-cdk/interfaces/nacl-config.d.ts +1 -0
  55. package/dist/aws-cdk/interfaces/nacl-config.js +3 -0
  56. package/dist/aws-cdk/interfaces/nlb-config.d.ts +69 -0
  57. package/dist/aws-cdk/interfaces/nlb-config.js +2 -0
  58. package/dist/aws-cdk/interfaces/rds-config.d.ts +84 -0
  59. package/dist/aws-cdk/interfaces/rds-config.js +7 -0
  60. package/dist/aws-cdk/interfaces/sqs-config.d.ts +145 -0
  61. package/dist/aws-cdk/interfaces/sqs-config.js +12 -0
  62. package/dist/aws-cdk/interfaces/tag-config.d.ts +18 -0
  63. package/dist/aws-cdk/interfaces/tag-config.js +2 -0
  64. package/dist/aws-cdk/interfaces/vpc-config.d.ts +72 -0
  65. package/dist/aws-cdk/interfaces/vpc-config.js +2 -0
  66. package/dist/aws-cdk/interfaces/waf-config.d.ts +180 -0
  67. package/dist/aws-cdk/interfaces/waf-config.js +2 -0
  68. package/dist/aws-cdk/utils/priority-tracker.d.ts +60 -0
  69. package/dist/aws-cdk/utils/priority-tracker.js +131 -0
  70. package/dist/index.d.ts +33 -0
  71. package/dist/index.js +55 -0
  72. package/dist/terraform-cdk/constructs/alb-listener-rule.d.ts +33 -0
  73. package/dist/terraform-cdk/constructs/alb-listener-rule.js +81 -0
  74. package/dist/terraform-cdk/constructs/ecs-service.d.ts +29 -0
  75. package/dist/terraform-cdk/constructs/ecs-service.js +238 -0
  76. package/dist/terraform-cdk/interfaces/ecs-service-config.d.ts +53 -0
  77. package/dist/terraform-cdk/interfaces/ecs-service-config.js +25 -0
  78. package/dist/terraform-cdk/interfaces/infrastructure-refs.d.ts +16 -0
  79. package/dist/terraform-cdk/interfaces/infrastructure-refs.js +8 -0
  80. package/dist/terraform-cdk/utils/priority-tracker.d.ts +60 -0
  81. package/dist/terraform-cdk/utils/priority-tracker.js +131 -0
  82. 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
+ }