@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,45 @@
1
+ import { Construct } from 'constructs';
2
+ import * as cloudfront from 'aws-cdk-lib/aws-cloudfront';
3
+ import { CloudFrontConfig } from '../interfaces/cloudfront-config';
4
+ import { VpcConstruct } from './vpc';
5
+ import { AlbConstruct } from './alb';
6
+ import { AcmConstruct } from './acm';
7
+ export interface CloudFrontConstructProps {
8
+ config: CloudFrontConfig;
9
+ /** VpcConstruct from NetworkStack */
10
+ vpcConstruct: VpcConstruct;
11
+ /** AlbConstruct from AlbStack — origin target */
12
+ albConstruct: AlbConstruct;
13
+ /** AcmConstruct from AcmStack — certificate สำหรับ CloudFront (us-east-1) */
14
+ acmConstruct?: AcmConstruct;
15
+ }
16
+ /**
17
+ * CloudFront + VPC Origin Construct
18
+ *
19
+ * สร้าง:
20
+ * 1. CloudFront VPC Origin — เชื่อมต่อ ALB private ผ่าน AWS backbone
21
+ * 2. CloudFront Distribution — CDN ด้านหน้า
22
+ * 3. (Optional) ALB SG rule — allow CloudFront traffic
23
+ *
24
+ * Architecture:
25
+ * Client → CloudFront Edge → VPC Origin → Private ALB → ECS
26
+ */
27
+ export declare class CloudFrontConstruct extends Construct {
28
+ readonly distribution: cloudfront.CfnDistribution;
29
+ readonly vpcOrigin: cloudfront.CfnVpcOrigin;
30
+ readonly distributionDomainName: string;
31
+ readonly distributionId: string;
32
+ readonly vpcOriginId: string;
33
+ private readonly removalPolicy;
34
+ constructor(scope: Construct, id: string, props: CloudFrontConstructProps);
35
+ private createVpcOrigin;
36
+ private createDistribution;
37
+ /**
38
+ * เพิ่ม inbound rule ให้ ALB SG เพื่อรับ traffic จาก CloudFront VPC Origin
39
+ *
40
+ * ถ้ามี cloudFrontPrefixListId → ใช้ managed prefix list (recommended สำหรับ production)
41
+ * ถ้าไม่มี → allow จาก 0.0.0.0/0 (ALB เป็น internal อยู่แล้ว ปลอดภัยในระดับหนึ่ง)
42
+ */
43
+ private addAlbSecurityGroupRule;
44
+ private createOutputs;
45
+ }
@@ -0,0 +1,261 @@
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.CloudFrontConstruct = 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 cloudfront = __importStar(require("aws-cdk-lib/aws-cloudfront"));
41
+ const ec2 = __importStar(require("aws-cdk-lib/aws-ec2"));
42
+ /**
43
+ * CloudFront + VPC Origin Construct
44
+ *
45
+ * สร้าง:
46
+ * 1. CloudFront VPC Origin — เชื่อมต่อ ALB private ผ่าน AWS backbone
47
+ * 2. CloudFront Distribution — CDN ด้านหน้า
48
+ * 3. (Optional) ALB SG rule — allow CloudFront traffic
49
+ *
50
+ * Architecture:
51
+ * Client → CloudFront Edge → VPC Origin → Private ALB → ECS
52
+ */
53
+ class CloudFrontConstruct extends constructs_1.Construct {
54
+ constructor(scope, id, props) {
55
+ super(scope, id);
56
+ const { config, vpcConstruct, albConstruct, acmConstruct } = props;
57
+ this.removalPolicy = config.removalPolicy === 'retain'
58
+ ? aws_cdk_lib_1.RemovalPolicy.RETAIN
59
+ : aws_cdk_lib_1.RemovalPolicy.DESTROY;
60
+ // ------------------------------------------
61
+ // A. Create VPC Origin
62
+ // ------------------------------------------
63
+ this.vpcOrigin = this.createVpcOrigin(config, albConstruct);
64
+ // ------------------------------------------
65
+ // B. Create CloudFront Distribution
66
+ // ------------------------------------------
67
+ this.distribution = this.createDistribution(config, albConstruct, acmConstruct);
68
+ // ------------------------------------------
69
+ // C. (Optional) Update ALB Security Group
70
+ // ------------------------------------------
71
+ if (config.updateAlbSecurityGroup !== false) {
72
+ this.addAlbSecurityGroupRule(config, albConstruct);
73
+ }
74
+ // ------------------------------------------
75
+ // D. Apply Removal Policy
76
+ // ------------------------------------------
77
+ this.vpcOrigin.applyRemovalPolicy(this.removalPolicy);
78
+ this.distribution.applyRemovalPolicy(this.removalPolicy);
79
+ // ------------------------------------------
80
+ // E. Expose Attributes
81
+ // ------------------------------------------
82
+ this.distributionDomainName = this.distribution.attrDomainName;
83
+ this.distributionId = this.distribution.ref;
84
+ this.vpcOriginId = this.vpcOrigin.attrId;
85
+ // ------------------------------------------
86
+ // F. Outputs
87
+ // ------------------------------------------
88
+ this.createOutputs(config);
89
+ }
90
+ // ==========================================
91
+ // A. VPC Origin
92
+ // ==========================================
93
+ createVpcOrigin(config, albConstruct) {
94
+ const vo = config.vpcOrigin;
95
+ return new cloudfront.CfnVpcOrigin(this, 'VpcOrigin', {
96
+ vpcOriginEndpointConfig: {
97
+ arn: albConstruct.alb.loadBalancerArn,
98
+ httpPort: vo.httpPort ?? 80,
99
+ httpsPort: vo.httpsPort ?? 443,
100
+ name: vo.name,
101
+ originProtocolPolicy: vo.originProtocolPolicy ?? 'https-only',
102
+ },
103
+ });
104
+ }
105
+ // ==========================================
106
+ // B. Distribution
107
+ // ==========================================
108
+ createDistribution(config, albConstruct, acmConstruct) {
109
+ const originId = 'vpc-alb-origin';
110
+ const voConfig = config.vpcOrigin;
111
+ const defaultBehavior = config.defaultCacheBehavior ?? {};
112
+ // --- Viewer Certificate ---
113
+ const certArn = acmConstruct?.certificateArn ?? config.certificateArn;
114
+ let viewerCertificate;
115
+ if (certArn && config.domainNames && config.domainNames.length > 0) {
116
+ viewerCertificate = {
117
+ acmCertificateArn: certArn,
118
+ minimumProtocolVersion: config.minimumProtocolVersion ?? 'TLSv1.2_2021',
119
+ sslSupportMethod: 'sni-only',
120
+ };
121
+ }
122
+ else {
123
+ viewerCertificate = {
124
+ cloudFrontDefaultCertificate: true,
125
+ };
126
+ }
127
+ // --- Default Cache Behavior ---
128
+ const defaultCacheBehavior = {
129
+ targetOriginId: originId,
130
+ viewerProtocolPolicy: defaultBehavior.viewerProtocolPolicy ?? 'redirect-to-https',
131
+ allowedMethods: defaultBehavior.allowedMethods
132
+ ?? ['GET', 'HEAD', 'OPTIONS', 'PUT', 'POST', 'PATCH', 'DELETE'],
133
+ cachedMethods: defaultBehavior.cachedMethods ?? ['GET', 'HEAD'],
134
+ // Default: CachingDisabled — เหมาะกับ dynamic content (ALB → ECS)
135
+ cachePolicyId: defaultBehavior.cachePolicyId
136
+ ?? '4135ea2d-6df8-44a3-9df3-4b5a84be39ad',
137
+ // Default: AllViewer — forward ทุก header ไป origin
138
+ originRequestPolicyId: defaultBehavior.originRequestPolicyId
139
+ ?? '216adef6-5c7f-47e4-b989-5492eafa07d3',
140
+ compress: defaultBehavior.compress ?? true,
141
+ responseHeadersPolicyId: defaultBehavior.responseHeadersPolicyId,
142
+ };
143
+ // --- Additional Cache Behaviors ---
144
+ const cacheBehaviors = config.additionalBehaviors?.map((ab) => ({
145
+ pathPattern: ab.pathPattern,
146
+ targetOriginId: originId,
147
+ viewerProtocolPolicy: ab.behavior.viewerProtocolPolicy ?? 'redirect-to-https',
148
+ allowedMethods: ab.behavior.allowedMethods ?? ['GET', 'HEAD'],
149
+ cachedMethods: ab.behavior.cachedMethods ?? ['GET', 'HEAD'],
150
+ // Default: CachingOptimized — เหมาะกับ static content
151
+ cachePolicyId: ab.behavior.cachePolicyId
152
+ ?? '658327ea-f89d-4fab-a63d-7e88639e58f6',
153
+ originRequestPolicyId: ab.behavior.originRequestPolicyId,
154
+ compress: ab.behavior.compress ?? true,
155
+ responseHeadersPolicyId: ab.behavior.responseHeadersPolicyId,
156
+ }));
157
+ // --- Custom Error Responses ---
158
+ const customErrorResponses = config.customErrorResponses?.map((err) => ({
159
+ errorCode: err.errorCode,
160
+ responseCode: err.responseCode,
161
+ responsePagePath: err.responsePagePath,
162
+ errorCachingMinTtl: err.errorCachingMinTtl,
163
+ }));
164
+ // --- Create Distribution ---
165
+ const distribution = new cloudfront.CfnDistribution(this, 'Distribution', {
166
+ distributionConfig: {
167
+ enabled: config.enabled ?? true,
168
+ comment: config.distributionComment
169
+ ?? `${config.stackName} — CloudFront + VPC Origin → Private ALB`,
170
+ httpVersion: config.httpVersion ?? 'http2and3',
171
+ priceClass: config.priceClass ?? 'PriceClass_200',
172
+ // Origin: ALB via VPC Origin
173
+ origins: [{
174
+ domainName: albConstruct.alb.loadBalancerDnsName,
175
+ id: originId,
176
+ // VPC Origin Config จะถูก add ผ่าน escape hatch ด้านล่าง
177
+ }],
178
+ defaultCacheBehavior,
179
+ cacheBehaviors,
180
+ viewerCertificate,
181
+ aliases: config.domainNames,
182
+ webAclId: config.webAclId,
183
+ customErrorResponses,
184
+ },
185
+ });
186
+ // --- Add VpcOriginConfig via escape hatch ---
187
+ // ใช้ addPropertyOverride เพื่อความเข้ากันได้กับ CDK ทุกเวอร์ชัน
188
+ distribution.addPropertyOverride('DistributionConfig.Origins.0.VpcOriginConfig', {
189
+ VpcOriginId: this.vpcOrigin.attrId,
190
+ OriginReadTimeout: voConfig.originReadTimeout ?? 30,
191
+ OriginKeepaliveTimeout: voConfig.originKeepaliveTimeout ?? 5,
192
+ });
193
+ // ลบ customOriginConfig / s3OriginConfig ที่อาจถูก generate อัตโนมัติ
194
+ distribution.addPropertyDeletionOverride('DistributionConfig.Origins.0.S3OriginConfig');
195
+ return distribution;
196
+ }
197
+ // ==========================================
198
+ // C. ALB Security Group Rule
199
+ // ==========================================
200
+ /**
201
+ * เพิ่ม inbound rule ให้ ALB SG เพื่อรับ traffic จาก CloudFront VPC Origin
202
+ *
203
+ * ถ้ามี cloudFrontPrefixListId → ใช้ managed prefix list (recommended สำหรับ production)
204
+ * ถ้าไม่มี → allow จาก 0.0.0.0/0 (ALB เป็น internal อยู่แล้ว ปลอดภัยในระดับหนึ่ง)
205
+ */
206
+ addAlbSecurityGroupRule(config, albConstruct) {
207
+ const httpsPort = config.vpcOrigin.httpsPort ?? 443;
208
+ const httpPort = config.vpcOrigin.httpPort ?? 80;
209
+ if (config.cloudFrontPrefixListId) {
210
+ // Use managed prefix list (more restrictive)
211
+ albConstruct.securityGroup.addIngressRule(ec2.Peer.prefixList(config.cloudFrontPrefixListId), ec2.Port.tcp(httpsPort), 'Allow HTTPS from CloudFront VPC Origin (managed prefix list)');
212
+ if (config.vpcOrigin.originProtocolPolicy === 'http-only') {
213
+ albConstruct.securityGroup.addIngressRule(ec2.Peer.prefixList(config.cloudFrontPrefixListId), ec2.Port.tcp(httpPort), 'Allow HTTP from CloudFront VPC Origin (managed prefix list)');
214
+ }
215
+ }
216
+ else {
217
+ // Fallback: allow from anywhere
218
+ // ⚠️ ALB เป็น internal (private subnets) จึงยังปลอดภัย
219
+ // สำหรับ production ควรระบุ cloudFrontPrefixListId
220
+ albConstruct.securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(httpsPort), 'Allow HTTPS from CloudFront VPC Origin');
221
+ if (config.vpcOrigin.originProtocolPolicy === 'http-only') {
222
+ albConstruct.securityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(httpPort), 'Allow HTTP from CloudFront VPC Origin');
223
+ }
224
+ }
225
+ }
226
+ // ==========================================
227
+ // F. Outputs
228
+ // ==========================================
229
+ createOutputs(config) {
230
+ const stack = cdk.Stack.of(this);
231
+ const prefix = config.stackName;
232
+ new cdk.CfnOutput(stack, 'DistributionId', {
233
+ value: this.distribution.ref,
234
+ description: 'CloudFront Distribution ID',
235
+ exportName: `${prefix}-CF-DistributionId`,
236
+ });
237
+ new cdk.CfnOutput(stack, 'DistributionDomainName', {
238
+ value: this.distribution.attrDomainName,
239
+ description: 'CloudFront Distribution Domain Name (*.cloudfront.net)',
240
+ exportName: `${prefix}-CF-DomainName`,
241
+ });
242
+ new cdk.CfnOutput(stack, 'VpcOriginId', {
243
+ value: this.vpcOrigin.attrId,
244
+ description: 'CloudFront VPC Origin ID',
245
+ exportName: `${prefix}-CF-VpcOriginId`,
246
+ });
247
+ new cdk.CfnOutput(stack, 'VpcOriginArn', {
248
+ value: this.vpcOrigin.attrArn,
249
+ description: 'CloudFront VPC Origin ARN',
250
+ exportName: `${prefix}-CF-VpcOriginArn`,
251
+ });
252
+ if (config.domainNames && config.domainNames.length > 0) {
253
+ new cdk.CfnOutput(stack, 'Aliases', {
254
+ value: config.domainNames.join(', '),
255
+ description: 'CloudFront Domain Aliases',
256
+ exportName: `${prefix}-CF-Aliases`,
257
+ });
258
+ }
259
+ }
260
+ }
261
+ exports.CloudFrontConstruct = CloudFrontConstruct;
@@ -0,0 +1,17 @@
1
+ import { Construct } from 'constructs';
2
+ import * as ecr from 'aws-cdk-lib/aws-ecr';
3
+ import { EcrConfig } from '../interfaces/ecr-config';
4
+ export interface EcrConstructProps {
5
+ config: EcrConfig;
6
+ }
7
+ /**
8
+ * ECR Construct - สร้าง ECR Repository + Lifecycle Rules
9
+ */
10
+ export declare class EcrConstruct extends Construct {
11
+ readonly repository: ecr.Repository;
12
+ private readonly removalPolicy;
13
+ constructor(scope: Construct, id: string, props: EcrConstructProps);
14
+ private createRepository;
15
+ private addLifecycleRules;
16
+ private createOutputs;
17
+ }
@@ -0,0 +1,143 @@
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.EcrConstruct = 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 ecr = __importStar(require("aws-cdk-lib/aws-ecr"));
41
+ /**
42
+ * ECR Construct - สร้าง ECR Repository + Lifecycle Rules
43
+ */
44
+ class EcrConstruct extends constructs_1.Construct {
45
+ constructor(scope, id, props) {
46
+ super(scope, id);
47
+ const { config } = props;
48
+ this.removalPolicy = config.removalPolicy === 'retain'
49
+ ? aws_cdk_lib_1.RemovalPolicy.RETAIN
50
+ : aws_cdk_lib_1.RemovalPolicy.DESTROY;
51
+ // A. Create Repository
52
+ this.repository = this.createRepository(config);
53
+ // B. Add Lifecycle Rules
54
+ this.addLifecycleRules(config);
55
+ // C. Apply Removal Policy
56
+ this.repository.applyRemovalPolicy(this.removalPolicy);
57
+ // D. Outputs
58
+ this.createOutputs(config);
59
+ }
60
+ // ==========================================
61
+ // Create Repository
62
+ // ==========================================
63
+ createRepository(config) {
64
+ const isDestroy = config.removalPolicy !== 'retain';
65
+ return new ecr.Repository(this, 'EcrRepository', {
66
+ repositoryName: config.repositoryName,
67
+ imageTagMutability: config.imageTagMutability === 'IMMUTABLE'
68
+ ? ecr.TagMutability.IMMUTABLE
69
+ : ecr.TagMutability.MUTABLE,
70
+ imageScanOnPush: config.scanOnPush ?? true,
71
+ removalPolicy: this.removalPolicy,
72
+ emptyOnDelete: config.emptyOnDelete ?? isDestroy,
73
+ });
74
+ }
75
+ // ==========================================
76
+ // Add Lifecycle Rules
77
+ // ==========================================
78
+ addLifecycleRules(config) {
79
+ // ถ้ามี lifecycleRules แบบ custom ใช้ตัวนั้น
80
+ if (config.lifecycleRules && config.lifecycleRules.length > 0) {
81
+ config.lifecycleRules.forEach((rule) => {
82
+ this.repository.addLifecycleRule({
83
+ rulePriority: rule.rulePriority,
84
+ description: rule.description,
85
+ tagStatus: rule.tagStatus === 'tagged'
86
+ ? ecr.TagStatus.TAGGED
87
+ : rule.tagStatus === 'untagged'
88
+ ? ecr.TagStatus.UNTAGGED
89
+ : ecr.TagStatus.ANY,
90
+ tagPrefixList: rule.tagPrefixList,
91
+ maxImageCount: rule.maxImageCount,
92
+ maxImageAge: rule.maxImageAgeDays
93
+ ? cdk.Duration.days(rule.maxImageAgeDays)
94
+ : undefined,
95
+ });
96
+ });
97
+ return;
98
+ }
99
+ // ถ้าไม่มี lifecycleRules ใช้ shorthand
100
+ const maxImages = config.lifecycleMaxImages ?? 30;
101
+ const daysUntagged = config.lifecycleDaysUntagged ?? 14;
102
+ // Rule 1: Keep last N tagged images
103
+ this.repository.addLifecycleRule({
104
+ rulePriority: 1,
105
+ description: `Keep last ${maxImages} tagged images`,
106
+ tagStatus: ecr.TagStatus.TAGGED,
107
+ tagPrefixList: ['v', 'latest', 'main', 'dev', 'prod', 'uat', 'pre-prod'],
108
+ maxImageCount: maxImages,
109
+ });
110
+ // Rule 2: Delete old untagged images
111
+ this.repository.addLifecycleRule({
112
+ rulePriority: 2,
113
+ description: `Delete untagged images older than ${daysUntagged} days`,
114
+ tagStatus: ecr.TagStatus.UNTAGGED,
115
+ maxImageAge: cdk.Duration.days(daysUntagged),
116
+ });
117
+ }
118
+ // ==========================================
119
+ // Outputs
120
+ // ==========================================
121
+ createOutputs(config) {
122
+ const stack = cdk.Stack.of(this);
123
+ const prefix = config.stackName;
124
+ // Use sanitized repo name for unique output IDs within same stack
125
+ const repoShort = config.repositoryName.replace(/[^a-zA-Z0-9]/g, '');
126
+ new cdk.CfnOutput(stack, `${repoShort}EcrRepositoryUri`, {
127
+ value: this.repository.repositoryUri,
128
+ description: `ECR Repository URI (${config.repositoryName})`,
129
+ exportName: `${prefix}-ECR-Uri`,
130
+ });
131
+ new cdk.CfnOutput(stack, `${repoShort}EcrRepositoryArn`, {
132
+ value: this.repository.repositoryArn,
133
+ description: `ECR Repository ARN (${config.repositoryName})`,
134
+ exportName: `${prefix}-ECR-Arn`,
135
+ });
136
+ new cdk.CfnOutput(stack, `${repoShort}EcrRepositoryName`, {
137
+ value: this.repository.repositoryName,
138
+ description: `ECR Repository Name (${config.repositoryName})`,
139
+ exportName: `${prefix}-ECR-Name`,
140
+ });
141
+ }
142
+ }
143
+ exports.EcrConstruct = EcrConstruct;
@@ -0,0 +1,21 @@
1
+ import { Construct } from 'constructs';
2
+ import * as ecs from 'aws-cdk-lib/aws-ecs';
3
+ import { EcsClusterConfig } from '../interfaces/ecs-cluster-config';
4
+ import { VpcConstruct } from './vpc';
5
+ export interface EcsClusterConstructProps {
6
+ config: EcsClusterConfig;
7
+ vpcConstruct: VpcConstruct;
8
+ }
9
+ /**
10
+ * ECS Cluster Construct - สร้าง ECS Cluster + Container Insights
11
+ *
12
+ * อ้างอิง VpcConstruct จาก NetworkStack เพื่อดึง VPC
13
+ */
14
+ export declare class EcsClusterConstruct extends Construct {
15
+ readonly cluster: ecs.Cluster;
16
+ private readonly removalPolicy;
17
+ constructor(scope: Construct, id: string, props: EcsClusterConstructProps);
18
+ private resolveVpc;
19
+ private createCluster;
20
+ private createOutputs;
21
+ }
@@ -0,0 +1,124 @@
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.EcsClusterConstruct = 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 ecs = __importStar(require("aws-cdk-lib/aws-ecs"));
42
+ /**
43
+ * ECS Cluster Construct - สร้าง ECS Cluster + Container Insights
44
+ *
45
+ * อ้างอิง VpcConstruct จาก NetworkStack เพื่อดึง VPC
46
+ */
47
+ class EcsClusterConstruct extends constructs_1.Construct {
48
+ constructor(scope, id, props) {
49
+ super(scope, id);
50
+ const { config, vpcConstruct } = props;
51
+ this.removalPolicy = config.removalPolicy === 'retain'
52
+ ? aws_cdk_lib_1.RemovalPolicy.RETAIN
53
+ : aws_cdk_lib_1.RemovalPolicy.DESTROY;
54
+ // A. Resolve VPC
55
+ const vpc = this.resolveVpc(vpcConstruct);
56
+ // B. Create Cluster
57
+ this.cluster = this.createCluster(config, vpc);
58
+ // C. Apply Removal Policy
59
+ this.cluster.applyRemovalPolicy(this.removalPolicy);
60
+ // D. Outputs
61
+ this.createOutputs(config);
62
+ }
63
+ // ==========================================
64
+ // Resolve VPC
65
+ // ==========================================
66
+ resolveVpc(vpcConstruct) {
67
+ // ดึง AZ จาก subnets ทั้งหมด
68
+ const allAzs = new Set();
69
+ vpcConstruct.subnetsByName.forEach((subnets) => {
70
+ subnets.forEach((s) => {
71
+ if (s.availabilityZone)
72
+ allAzs.add(s.availabilityZone);
73
+ });
74
+ });
75
+ return ec2.Vpc.fromVpcAttributes(this, 'Vpc', {
76
+ vpcId: vpcConstruct.vpc.ref,
77
+ availabilityZones: Array.from(allAzs),
78
+ });
79
+ }
80
+ // ==========================================
81
+ // Create Cluster
82
+ // ==========================================
83
+ createCluster(config, vpc) {
84
+ const insightsEnabled = config.containerInsightsEnabled ?? true;
85
+ const cluster = new ecs.Cluster(this, 'EcsCluster', {
86
+ vpc,
87
+ clusterName: config.clusterName,
88
+ containerInsightsV2: insightsEnabled
89
+ ? ecs.ContainerInsights.ENHANCED
90
+ : ecs.ContainerInsights.DISABLED,
91
+ enableFargateCapacityProviders: true,
92
+ executeCommandConfiguration: config.enableExecuteCommand
93
+ ? { logging: ecs.ExecuteCommandLogging.DEFAULT }
94
+ : undefined,
95
+ });
96
+ // Default capacity provider strategy (ต้องส่ง array ทั้งหมดในครั้งเดียว)
97
+ if (config.defaultCapacityProviderStrategy && config.defaultCapacityProviderStrategy.length > 0) {
98
+ cluster.addDefaultCapacityProviderStrategy(config.defaultCapacityProviderStrategy.map((strategy) => ({
99
+ capacityProvider: strategy.capacityProvider,
100
+ weight: strategy.weight,
101
+ base: strategy.base,
102
+ })));
103
+ }
104
+ return cluster;
105
+ }
106
+ // ==========================================
107
+ // Outputs
108
+ // ==========================================
109
+ createOutputs(config) {
110
+ const stack = cdk.Stack.of(this);
111
+ const prefix = config.stackName;
112
+ new cdk.CfnOutput(stack, 'EcsClusterName', {
113
+ value: this.cluster.clusterName,
114
+ description: 'ECS Cluster Name',
115
+ exportName: `${prefix}-ECS-ClusterName`,
116
+ });
117
+ new cdk.CfnOutput(stack, 'EcsClusterArn', {
118
+ value: this.cluster.clusterArn,
119
+ description: 'ECS Cluster ARN',
120
+ exportName: `${prefix}-ECS-ClusterArn`,
121
+ });
122
+ }
123
+ }
124
+ exports.EcsClusterConstruct = EcsClusterConstruct;