@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,83 @@
1
+ import { Construct } from 'constructs';
2
+ import * as acm from 'aws-cdk-lib/aws-certificatemanager';
3
+ export interface SelfSignedCertificateConstructProps {
4
+ /**
5
+ * Domain name for the certificate (CN)
6
+ * Example: 'example.local', '*.example.local'
7
+ */
8
+ domainName: string;
9
+ /**
10
+ * Certificate validity in days
11
+ * Default: 365
12
+ */
13
+ validityDays?: number;
14
+ /**
15
+ * Organization name
16
+ * Default: 'Demo'
17
+ */
18
+ organization?: string;
19
+ /**
20
+ * Country code (2 letters)
21
+ * Default: 'TH'
22
+ */
23
+ country?: string;
24
+ /**
25
+ * Removal policy
26
+ * Default: 'destroy'
27
+ */
28
+ removalPolicy?: 'destroy' | 'retain';
29
+ /**
30
+ * Stack name for CloudFormation outputs
31
+ */
32
+ stackName: string;
33
+ }
34
+ /**
35
+ * Self-Signed Certificate Construct
36
+ *
37
+ * Generates a self-signed SSL/TLS certificate and imports it to ACM.
38
+ *
39
+ * Features:
40
+ * - Generates certificate during CDK synth (no runtime dependencies)
41
+ * - Imports to ACM using Custom Resource
42
+ * - Auto-deletes certificate when stack is destroyed
43
+ * - Suitable for dev/test environments
44
+ *
45
+ * ⚠️ WARNING: Self-signed certificates are NOT trusted by browsers.
46
+ * Use only for development/testing. For production, use ACM request mode
47
+ * or import certificates from a trusted CA.
48
+ *
49
+ * Example:
50
+ * ```typescript
51
+ * const cert = new SelfSignedCertificateConstruct(this, 'SelfSignedCert', {
52
+ * stackName: 'my-stack',
53
+ * domainName: 'app.local',
54
+ * validityDays: 365,
55
+ * });
56
+ *
57
+ * // Use with ALB
58
+ * const alb = new AlbConstruct(this, 'Alb', {
59
+ * config: {
60
+ * enableHttps: true,
61
+ * certificateArn: cert.certificateArn,
62
+ * }
63
+ * });
64
+ * ```
65
+ */
66
+ export declare class SelfSignedCertificateConstruct extends Construct {
67
+ readonly certificate: acm.ICertificate;
68
+ readonly certificateArn: string;
69
+ private readonly removalPolicy;
70
+ constructor(scope: Construct, id: string, props: SelfSignedCertificateConstructProps);
71
+ /**
72
+ * Generate self-signed certificate using OpenSSL
73
+ *
74
+ * Certificate is cached in .cdk-selfsigned/ directory to ensure
75
+ * consistent values across synth runs (prevents cross-stack export changes).
76
+ * Delete the cache directory to force regeneration.
77
+ */
78
+ private generateCertificate;
79
+ /**
80
+ * Create CloudFormation outputs
81
+ */
82
+ private createOutputs;
83
+ }
@@ -0,0 +1,215 @@
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.SelfSignedCertificateConstruct = 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 acm = __importStar(require("aws-cdk-lib/aws-certificatemanager"));
41
+ const cr = __importStar(require("aws-cdk-lib/custom-resources"));
42
+ const iam = __importStar(require("aws-cdk-lib/aws-iam"));
43
+ const child_process_1 = require("child_process");
44
+ const fs_1 = require("fs");
45
+ const path_1 = require("path");
46
+ const os_1 = require("os");
47
+ /**
48
+ * Self-Signed Certificate Construct
49
+ *
50
+ * Generates a self-signed SSL/TLS certificate and imports it to ACM.
51
+ *
52
+ * Features:
53
+ * - Generates certificate during CDK synth (no runtime dependencies)
54
+ * - Imports to ACM using Custom Resource
55
+ * - Auto-deletes certificate when stack is destroyed
56
+ * - Suitable for dev/test environments
57
+ *
58
+ * ⚠️ WARNING: Self-signed certificates are NOT trusted by browsers.
59
+ * Use only for development/testing. For production, use ACM request mode
60
+ * or import certificates from a trusted CA.
61
+ *
62
+ * Example:
63
+ * ```typescript
64
+ * const cert = new SelfSignedCertificateConstruct(this, 'SelfSignedCert', {
65
+ * stackName: 'my-stack',
66
+ * domainName: 'app.local',
67
+ * validityDays: 365,
68
+ * });
69
+ *
70
+ * // Use with ALB
71
+ * const alb = new AlbConstruct(this, 'Alb', {
72
+ * config: {
73
+ * enableHttps: true,
74
+ * certificateArn: cert.certificateArn,
75
+ * }
76
+ * });
77
+ * ```
78
+ */
79
+ class SelfSignedCertificateConstruct extends constructs_1.Construct {
80
+ constructor(scope, id, props) {
81
+ super(scope, id);
82
+ this.removalPolicy = props.removalPolicy === 'retain'
83
+ ? aws_cdk_lib_1.RemovalPolicy.RETAIN
84
+ : aws_cdk_lib_1.RemovalPolicy.DESTROY;
85
+ // Generate certificate during synth
86
+ const { certBody, privateKey } = this.generateCertificate(props);
87
+ // Import to ACM using Custom Resource
88
+ const importCert = new cr.AwsCustomResource(this, 'ImportCertificate', {
89
+ onCreate: {
90
+ service: 'ACM',
91
+ action: 'importCertificate',
92
+ parameters: {
93
+ Certificate: certBody,
94
+ PrivateKey: privateKey,
95
+ },
96
+ physicalResourceId: cr.PhysicalResourceId.fromResponse('CertificateArn'),
97
+ },
98
+ onUpdate: {
99
+ service: 'ACM',
100
+ action: 'importCertificate',
101
+ parameters: {
102
+ Certificate: certBody,
103
+ PrivateKey: privateKey,
104
+ },
105
+ physicalResourceId: cr.PhysicalResourceId.fromResponse('CertificateArn'),
106
+ },
107
+ onDelete: {
108
+ service: 'ACM',
109
+ action: 'deleteCertificate',
110
+ parameters: {
111
+ CertificateArn: new cr.PhysicalResourceIdReference(),
112
+ },
113
+ },
114
+ policy: cr.AwsCustomResourcePolicy.fromStatements([
115
+ new iam.PolicyStatement({
116
+ actions: [
117
+ 'acm:ImportCertificate',
118
+ 'acm:DeleteCertificate',
119
+ 'acm:DescribeCertificate',
120
+ ],
121
+ resources: ['*'],
122
+ }),
123
+ ]),
124
+ });
125
+ // Get certificate ARN from response
126
+ this.certificateArn = importCert.getResponseField('CertificateArn');
127
+ this.certificate = acm.Certificate.fromCertificateArn(this, 'Certificate', this.certificateArn);
128
+ // Outputs
129
+ this.createOutputs(props);
130
+ }
131
+ /**
132
+ * Generate self-signed certificate using OpenSSL
133
+ *
134
+ * Certificate is cached in .cdk-selfsigned/ directory to ensure
135
+ * consistent values across synth runs (prevents cross-stack export changes).
136
+ * Delete the cache directory to force regeneration.
137
+ */
138
+ generateCertificate(props) {
139
+ const validityDays = props.validityDays ?? 365;
140
+ const organization = props.organization ?? 'Demo';
141
+ const country = props.country ?? 'TH';
142
+ // Cache directory: .cdk-selfsigned/<stackName>/ in project root
143
+ const projectRoot = process.cwd();
144
+ const cacheDir = (0, path_1.join)(projectRoot, '.cdk-selfsigned', props.stackName);
145
+ const cachedKeyFile = (0, path_1.join)(cacheDir, 'key.pem');
146
+ const cachedCertFile = (0, path_1.join)(cacheDir, 'cert.pem');
147
+ // Use cached certificate if exists
148
+ if ((0, fs_1.existsSync)(cachedKeyFile) && (0, fs_1.existsSync)(cachedCertFile)) {
149
+ console.log(`🔐 Using cached self-signed certificate for ${props.domainName}`);
150
+ console.log(` Cache: ${cacheDir}`);
151
+ const certBody = (0, fs_1.readFileSync)(cachedCertFile, 'utf8');
152
+ const privateKey = (0, fs_1.readFileSync)(cachedKeyFile, 'utf8');
153
+ return { certBody, privateKey };
154
+ }
155
+ // Generate new certificate
156
+ const tmpDir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'cdk-selfsigned-'));
157
+ const keyFile = (0, path_1.join)(tmpDir, 'key.pem');
158
+ const certFile = (0, path_1.join)(tmpDir, 'cert.pem');
159
+ try {
160
+ console.log(`🔐 Generating self-signed certificate for ${props.domainName}...`);
161
+ // Generate private key (2048-bit RSA)
162
+ (0, child_process_1.execSync)(`openssl genrsa 2048 > "${keyFile}"`, { stdio: 'pipe' });
163
+ // Generate self-signed certificate
164
+ const subject = `/CN=${props.domainName}/O=${organization}/C=${country}`;
165
+ (0, child_process_1.execSync)(`openssl req -new -x509 -key "${keyFile}" -out "${certFile}" -days ${validityDays} -subj "${subject}"`, { stdio: 'pipe' });
166
+ // Read certificate and key
167
+ const certBody = (0, fs_1.readFileSync)(certFile, 'utf8');
168
+ const privateKey = (0, fs_1.readFileSync)(keyFile, 'utf8');
169
+ // Save to cache
170
+ (0, fs_1.mkdirSync)(cacheDir, { recursive: true });
171
+ (0, fs_1.writeFileSync)(cachedKeyFile, privateKey, { mode: 0o600 });
172
+ (0, fs_1.writeFileSync)(cachedCertFile, certBody);
173
+ console.log(`✅ Certificate generated and cached`);
174
+ console.log(` Domain: ${props.domainName}`);
175
+ console.log(` Valid for: ${validityDays} days`);
176
+ console.log(` Cache: ${cacheDir}`);
177
+ console.log(` ⚠️ Delete cache to regenerate: rm -rf ${cacheDir}`);
178
+ return { certBody, privateKey };
179
+ }
180
+ catch (error) {
181
+ throw new Error(`Failed to generate self-signed certificate: ${error}\n` +
182
+ `Make sure OpenSSL is installed and available in PATH.`);
183
+ }
184
+ finally {
185
+ // Cleanup temp files
186
+ try {
187
+ (0, fs_1.rmSync)(tmpDir, { recursive: true, force: true });
188
+ }
189
+ catch (e) {
190
+ // Ignore cleanup errors
191
+ }
192
+ }
193
+ }
194
+ /**
195
+ * Create CloudFormation outputs
196
+ */
197
+ createOutputs(props) {
198
+ const stack = cdk.Stack.of(this);
199
+ new cdk.CfnOutput(stack, 'SelfSignedCertificateArn', {
200
+ value: this.certificateArn,
201
+ description: `Self-signed certificate ARN for ${props.domainName}`,
202
+ exportName: `${props.stackName}-SelfSignedCert-Arn`,
203
+ });
204
+ new cdk.CfnOutput(stack, 'SelfSignedCertificateDomain', {
205
+ value: props.domainName,
206
+ description: 'Certificate domain name',
207
+ exportName: `${props.stackName}-SelfSignedCert-Domain`,
208
+ });
209
+ new cdk.CfnOutput(stack, 'SelfSignedCertificateValidity', {
210
+ value: `${props.validityDays ?? 365} days`,
211
+ description: 'Certificate validity period',
212
+ });
213
+ }
214
+ }
215
+ exports.SelfSignedCertificateConstruct = SelfSignedCertificateConstruct;
@@ -0,0 +1,30 @@
1
+ import { Construct } from 'constructs';
2
+ import * as sqs from 'aws-cdk-lib/aws-sqs';
3
+ import { SqsConfig } from '../interfaces/sqs-config';
4
+ export interface SqsConstructProps {
5
+ config: SqsConfig;
6
+ }
7
+ /**
8
+ * SQS Construct — สร้าง SQS Queue + Dead Letter Queue
9
+ *
10
+ * สร้าง:
11
+ * 1. Main Queue (Standard หรือ FIFO)
12
+ * 2. Dead Letter Queue (optional) — รับ message ที่ process ไม่สำเร็จ
13
+ * 3. Access Policy (optional) — cross-account permissions
14
+ * 4. Redrive Allow Policy (optional) — สำหรับ DLQ
15
+ */
16
+ export declare class SqsConstruct extends Construct {
17
+ readonly queue: sqs.Queue;
18
+ readonly deadLetterQueue?: sqs.Queue;
19
+ readonly queueArn: string;
20
+ readonly queueUrl: string;
21
+ readonly deadLetterQueueArn?: string;
22
+ readonly deadLetterQueueUrl?: string;
23
+ private readonly removalPolicy;
24
+ constructor(scope: Construct, id: string, props: SqsConstructProps);
25
+ private createDeadLetterQueue;
26
+ private createMainQueue;
27
+ private resolveEncryption;
28
+ private addAccessPolicies;
29
+ private createOutputs;
30
+ }
@@ -0,0 +1,268 @@
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.SqsConstruct = 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 sqs = __importStar(require("aws-cdk-lib/aws-sqs"));
41
+ const kms = __importStar(require("aws-cdk-lib/aws-kms"));
42
+ const iam = __importStar(require("aws-cdk-lib/aws-iam"));
43
+ /**
44
+ * SQS Construct — สร้าง SQS Queue + Dead Letter Queue
45
+ *
46
+ * สร้าง:
47
+ * 1. Main Queue (Standard หรือ FIFO)
48
+ * 2. Dead Letter Queue (optional) — รับ message ที่ process ไม่สำเร็จ
49
+ * 3. Access Policy (optional) — cross-account permissions
50
+ * 4. Redrive Allow Policy (optional) — สำหรับ DLQ
51
+ */
52
+ class SqsConstruct extends constructs_1.Construct {
53
+ constructor(scope, id, props) {
54
+ super(scope, id);
55
+ const { config } = props;
56
+ this.removalPolicy = config.removalPolicy === 'retain'
57
+ ? aws_cdk_lib_1.RemovalPolicy.RETAIN
58
+ : aws_cdk_lib_1.RemovalPolicy.DESTROY;
59
+ // ------------------------------------------
60
+ // A. Create Dead Letter Queue (ต้องสร้างก่อน Main Queue)
61
+ // ------------------------------------------
62
+ if (config.deadLetterQueue) {
63
+ this.deadLetterQueue = this.createDeadLetterQueue(config);
64
+ this.deadLetterQueueArn = this.deadLetterQueue.queueArn;
65
+ this.deadLetterQueueUrl = this.deadLetterQueue.queueUrl;
66
+ }
67
+ // ------------------------------------------
68
+ // B. Create Main Queue
69
+ // ------------------------------------------
70
+ this.queue = this.createMainQueue(config);
71
+ this.queueArn = this.queue.queueArn;
72
+ this.queueUrl = this.queue.queueUrl;
73
+ // ------------------------------------------
74
+ // C. Access Policies
75
+ // ------------------------------------------
76
+ this.addAccessPolicies(config);
77
+ // ------------------------------------------
78
+ // D. Apply Removal Policy
79
+ // ------------------------------------------
80
+ this.queue.applyRemovalPolicy(this.removalPolicy);
81
+ if (this.deadLetterQueue) {
82
+ this.deadLetterQueue.applyRemovalPolicy(this.removalPolicy);
83
+ }
84
+ // ------------------------------------------
85
+ // E. Outputs
86
+ // ------------------------------------------
87
+ this.createOutputs(config);
88
+ }
89
+ // ==========================================
90
+ // A. Create Dead Letter Queue
91
+ // ==========================================
92
+ createDeadLetterQueue(config) {
93
+ const dlqConfig = config.deadLetterQueue;
94
+ const fifo = config.fifo ?? false;
95
+ // DLQ name: custom หรือ <queueName>-dlq
96
+ let dlqName = dlqConfig.queueName ?? `${config.queueName}-dlq`;
97
+ if (fifo && !dlqName.endsWith('.fifo')) {
98
+ dlqName += '.fifo';
99
+ }
100
+ // Encryption
101
+ const encryption = this.resolveEncryption(config);
102
+ const dlq = new sqs.Queue(this, 'DeadLetterQueue', {
103
+ queueName: dlqName,
104
+ fifo,
105
+ retentionPeriod: cdk.Duration.seconds(dlqConfig.retentionPeriod ?? 1209600), // 14 วัน
106
+ visibilityTimeout: cdk.Duration.seconds(dlqConfig.visibilityTimeout ?? config.visibilityTimeout ?? 30),
107
+ encryption: encryption.type,
108
+ encryptionMasterKey: encryption.key,
109
+ dataKeyReuse: encryption.dataKeyReuse,
110
+ });
111
+ return dlq;
112
+ }
113
+ // ==========================================
114
+ // B. Create Main Queue
115
+ // ==========================================
116
+ createMainQueue(config) {
117
+ const fifo = config.fifo ?? false;
118
+ // Queue name
119
+ let queueName = config.queueName;
120
+ if (fifo && !queueName.endsWith('.fifo')) {
121
+ queueName += '.fifo';
122
+ }
123
+ // Encryption
124
+ const encryption = this.resolveEncryption(config);
125
+ // Dead Letter Queue settings
126
+ let deadLetterQueue;
127
+ if (this.deadLetterQueue && config.deadLetterQueue) {
128
+ deadLetterQueue = {
129
+ queue: this.deadLetterQueue,
130
+ maxReceiveCount: config.deadLetterQueue.maxReceiveCount ?? 3,
131
+ };
132
+ }
133
+ const queue = new sqs.Queue(this, 'Queue', {
134
+ queueName,
135
+ fifo,
136
+ contentBasedDeduplication: fifo ? (config.contentBasedDeduplication ?? false) : undefined,
137
+ visibilityTimeout: cdk.Duration.seconds(config.visibilityTimeout ?? 30),
138
+ retentionPeriod: cdk.Duration.seconds(config.retentionPeriod ?? 345600), // 4 วัน
139
+ deliveryDelay: cdk.Duration.seconds(config.deliveryDelay ?? 0),
140
+ receiveMessageWaitTime: cdk.Duration.seconds(config.receiveMessageWaitTime ?? 0),
141
+ maxMessageSizeBytes: config.maxMessageSize ?? 262144, // 256 KB
142
+ deadLetterQueue,
143
+ encryption: encryption.type,
144
+ encryptionMasterKey: encryption.key,
145
+ dataKeyReuse: encryption.dataKeyReuse,
146
+ });
147
+ // FIFO High Throughput — ตั้งค่าผ่าน L1 escape hatch
148
+ if (fifo && config.highThroughputFifo) {
149
+ const cfnQueue = queue.node.defaultChild;
150
+ cfnQueue.addPropertyOverride('DeduplicationScope', 'messageGroup');
151
+ cfnQueue.addPropertyOverride('FifoThroughputLimit', 'perMessageGroupId');
152
+ }
153
+ else if (fifo && config.fifoThroughputLimit === 'perMessageGroupId') {
154
+ const cfnQueue = queue.node.defaultChild;
155
+ cfnQueue.addPropertyOverride('FifoThroughputLimit', 'perMessageGroupId');
156
+ }
157
+ return queue;
158
+ }
159
+ // ==========================================
160
+ // Encryption Helper
161
+ // ==========================================
162
+ resolveEncryption(config) {
163
+ const encConfig = config.encryption;
164
+ const encType = encConfig?.type ?? 'sqs-managed';
165
+ if (encType === 'kms' && encConfig?.kmsKeyArn) {
166
+ return {
167
+ type: sqs.QueueEncryption.KMS,
168
+ key: kms.Key.fromKeyArn(this, 'EncryptionKey', encConfig.kmsKeyArn),
169
+ dataKeyReuse: cdk.Duration.seconds(encConfig.kmsDataKeyReusePeriod ?? 300),
170
+ };
171
+ }
172
+ if (encType === 'none') {
173
+ return { type: sqs.QueueEncryption.UNENCRYPTED };
174
+ }
175
+ // Default: SQS managed
176
+ return { type: sqs.QueueEncryption.SQS_MANAGED };
177
+ }
178
+ // ==========================================
179
+ // C. Access Policies
180
+ // ==========================================
181
+ addAccessPolicies(config) {
182
+ // Allow SendMessage from specific accounts
183
+ if (config.allowSendFromAccounts && config.allowSendFromAccounts.length > 0) {
184
+ const principals = config.allowSendFromAccounts.map((accountId) => new iam.AccountPrincipal(accountId));
185
+ this.queue.addToResourcePolicy(new iam.PolicyStatement({
186
+ sid: 'AllowSendFromAccounts',
187
+ effect: iam.Effect.ALLOW,
188
+ principals,
189
+ actions: ['sqs:SendMessage'],
190
+ resources: [this.queue.queueArn],
191
+ }));
192
+ }
193
+ // Allow ReceiveMessage + DeleteMessage from specific accounts
194
+ if (config.allowReceiveFromAccounts && config.allowReceiveFromAccounts.length > 0) {
195
+ const principals = config.allowReceiveFromAccounts.map((accountId) => new iam.AccountPrincipal(accountId));
196
+ this.queue.addToResourcePolicy(new iam.PolicyStatement({
197
+ sid: 'AllowReceiveFromAccounts',
198
+ effect: iam.Effect.ALLOW,
199
+ principals,
200
+ actions: [
201
+ 'sqs:ReceiveMessage',
202
+ 'sqs:DeleteMessage',
203
+ 'sqs:GetQueueAttributes',
204
+ ],
205
+ resources: [this.queue.queueArn],
206
+ }));
207
+ }
208
+ // Redrive allow policy on DLQ
209
+ if (this.deadLetterQueue && config.redriveAllowPolicy) {
210
+ const rap = config.redriveAllowPolicy;
211
+ if (rap.redrivePermission === 'denyAll') {
212
+ // Deny all queues from using this as DLQ
213
+ const cfnDlq = this.deadLetterQueue.node.defaultChild;
214
+ cfnDlq.addPropertyOverride('RedriveAllowPolicy', {
215
+ redrivePermission: 'denyAll',
216
+ });
217
+ }
218
+ else if (rap.redrivePermission === 'byQueue' && rap.sourceQueueArns) {
219
+ const cfnDlq = this.deadLetterQueue.node.defaultChild;
220
+ cfnDlq.addPropertyOverride('RedriveAllowPolicy', {
221
+ redrivePermission: 'byQueue',
222
+ sourceQueueArns: rap.sourceQueueArns,
223
+ });
224
+ }
225
+ // 'allowAll' is the default — ไม่ต้องตั้งค่า
226
+ }
227
+ }
228
+ // ==========================================
229
+ // E. Outputs
230
+ // ==========================================
231
+ createOutputs(config) {
232
+ const stack = cdk.Stack.of(this);
233
+ const prefix = config.stackName;
234
+ new cdk.CfnOutput(stack, 'QueueUrl', {
235
+ value: this.queue.queueUrl,
236
+ description: 'SQS Queue URL',
237
+ exportName: `${prefix}-SQS-QueueUrl`,
238
+ });
239
+ new cdk.CfnOutput(stack, 'QueueArn', {
240
+ value: this.queue.queueArn,
241
+ description: 'SQS Queue ARN',
242
+ exportName: `${prefix}-SQS-QueueArn`,
243
+ });
244
+ new cdk.CfnOutput(stack, 'QueueName', {
245
+ value: this.queue.queueName,
246
+ description: 'SQS Queue Name',
247
+ exportName: `${prefix}-SQS-QueueName`,
248
+ });
249
+ if (this.deadLetterQueue) {
250
+ new cdk.CfnOutput(stack, 'DlqUrl', {
251
+ value: this.deadLetterQueue.queueUrl,
252
+ description: 'Dead Letter Queue URL',
253
+ exportName: `${prefix}-SQS-DlqUrl`,
254
+ });
255
+ new cdk.CfnOutput(stack, 'DlqArn', {
256
+ value: this.deadLetterQueue.queueArn,
257
+ description: 'Dead Letter Queue ARN',
258
+ exportName: `${prefix}-SQS-DlqArn`,
259
+ });
260
+ new cdk.CfnOutput(stack, 'DlqName', {
261
+ value: this.deadLetterQueue.queueName,
262
+ description: 'Dead Letter Queue Name',
263
+ exportName: `${prefix}-SQS-DlqName`,
264
+ });
265
+ }
266
+ }
267
+ }
268
+ exports.SqsConstruct = SqsConstruct;
@@ -0,0 +1,30 @@
1
+ import { Construct } from 'constructs';
2
+ import * as ec2 from 'aws-cdk-lib/aws-ec2';
3
+ import { VpcConfig } from '../interfaces/vpc-config';
4
+ export interface VpcConstructProps {
5
+ config: VpcConfig;
6
+ }
7
+ export declare class VpcConstruct extends Construct {
8
+ readonly vpc: ec2.CfnVPC;
9
+ readonly subnets: Map<string, ec2.CfnSubnet>;
10
+ readonly subnetsByName: Map<string, ec2.CfnSubnet[]>;
11
+ readonly routeTables: Map<string, ec2.CfnRouteTable>;
12
+ /** Map: subnetKey (name-az) → routeTableId (CfnRouteTable.ref) */
13
+ readonly subnetRouteTableMap: Map<string, string>;
14
+ readonly natGateways: ec2.CfnNatGateway[];
15
+ private igw?;
16
+ private readonly removalPolicy;
17
+ constructor(scope: Construct, id: string, props: VpcConstructProps);
18
+ private createRoutesForRouteTable;
19
+ private createDefaultRoutes;
20
+ private createRoute;
21
+ /**
22
+ * Get routeTableId for a subnet by name and az
23
+ * ลองหา key "name-az" ก่อน, ถ้าไม่เจอ fallback ไป "name" (routeTablePerAz: false)
24
+ */
25
+ getRouteTableId(subnetName: string, az: string): string | undefined;
26
+ /**
27
+ * Export VPC and subnet IDs for cross-stack references (e.g., Network Security Stack)
28
+ */
29
+ exportForSecurityStack(stackName: string): void;
30
+ }