cdk-factory 0.17.6__py3-none-any.whl → 0.20.0__py3-none-any.whl
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.
Potentially problematic release.
This version of cdk-factory might be problematic. Click here for more details.
- cdk_factory/configurations/deployment.py +12 -0
- cdk_factory/configurations/resources/acm.py +9 -2
- cdk_factory/configurations/resources/auto_scaling.py +7 -5
- cdk_factory/configurations/resources/ecs_cluster.py +5 -0
- cdk_factory/configurations/resources/ecs_service.py +24 -2
- cdk_factory/configurations/resources/lambda_edge.py +18 -4
- cdk_factory/configurations/resources/rds.py +1 -1
- cdk_factory/configurations/resources/route53.py +5 -0
- cdk_factory/configurations/resources/s3.py +9 -1
- cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py +1 -1
- cdk_factory/constructs/lambdas/policies/policy_docs.py +1 -1
- cdk_factory/interfaces/networked_stack_mixin.py +1 -1
- cdk_factory/interfaces/standardized_ssm_mixin.py +82 -10
- cdk_factory/stack_library/acm/acm_stack.py +5 -15
- cdk_factory/stack_library/api_gateway/api_gateway_stack.py +2 -2
- cdk_factory/stack_library/auto_scaling/{auto_scaling_stack_standardized.py → auto_scaling_stack.py} +213 -105
- cdk_factory/stack_library/cloudfront/cloudfront_stack.py +76 -22
- cdk_factory/stack_library/code_artifact/code_artifact_stack.py +3 -25
- cdk_factory/stack_library/cognito/cognito_stack.py +2 -2
- cdk_factory/stack_library/dynamodb/dynamodb_stack.py +2 -2
- cdk_factory/stack_library/ecs/__init__.py +2 -4
- cdk_factory/stack_library/ecs/{ecs_cluster_stack_standardized.py → ecs_cluster_stack.py} +52 -41
- cdk_factory/stack_library/ecs/ecs_service_stack.py +49 -26
- cdk_factory/stack_library/lambda_edge/EDGE_LOG_RETENTION_TODO.md +226 -0
- cdk_factory/stack_library/lambda_edge/LAMBDA_EDGE_LOG_RETENTION_BLOG.md +215 -0
- cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +238 -81
- cdk_factory/stack_library/load_balancer/load_balancer_stack.py +128 -177
- cdk_factory/stack_library/rds/rds_stack.py +65 -72
- cdk_factory/stack_library/route53/route53_stack.py +244 -38
- cdk_factory/stack_library/rum/rum_stack.py +3 -3
- cdk_factory/stack_library/security_group/security_group_full_stack.py +1 -31
- cdk_factory/stack_library/security_group/security_group_stack.py +1 -8
- cdk_factory/stack_library/simple_queue_service/sqs_stack.py +1 -34
- cdk_factory/stack_library/stack_base.py +5 -0
- cdk_factory/stack_library/vpc/{vpc_stack_standardized.py → vpc_stack.py} +6 -109
- cdk_factory/stack_library/websites/static_website_stack.py +7 -3
- cdk_factory/utilities/api_gateway_integration_utility.py +2 -2
- cdk_factory/utilities/environment_services.py +2 -2
- cdk_factory/version.py +1 -1
- {cdk_factory-0.17.6.dist-info → cdk_factory-0.20.0.dist-info}/METADATA +1 -1
- {cdk_factory-0.17.6.dist-info → cdk_factory-0.20.0.dist-info}/RECORD +44 -42
- {cdk_factory-0.17.6.dist-info → cdk_factory-0.20.0.dist-info}/WHEEL +0 -0
- {cdk_factory-0.17.6.dist-info → cdk_factory-0.20.0.dist-info}/entry_points.txt +0 -0
- {cdk_factory-0.17.6.dist-info → cdk_factory-0.20.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -7,10 +7,15 @@ MIT License. See Project Root for the license information.
|
|
|
7
7
|
from typing import Dict, Any, List, Optional
|
|
8
8
|
|
|
9
9
|
import aws_cdk as cdk
|
|
10
|
-
from aws_cdk import
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
from aws_cdk import (
|
|
11
|
+
aws_route53 as route53,
|
|
12
|
+
aws_route53_targets as targets,
|
|
13
|
+
aws_certificatemanager as acm,
|
|
14
|
+
aws_elasticloadbalancingv2 as elbv2,
|
|
15
|
+
aws_cloudfront as cloudfront,
|
|
16
|
+
Duration,
|
|
17
|
+
CfnOutput,
|
|
18
|
+
)
|
|
14
19
|
from aws_lambda_powertools import Logger
|
|
15
20
|
from constructs import Construct
|
|
16
21
|
|
|
@@ -35,6 +40,7 @@ class Route53Stack(IStack, StandardizedSsmMixin):
|
|
|
35
40
|
|
|
36
41
|
def __init__(self, scope: Construct, id: str, **kwargs) -> None:
|
|
37
42
|
super().__init__(scope, id, **kwargs)
|
|
43
|
+
|
|
38
44
|
self.route53_config = None
|
|
39
45
|
self.stack_config = None
|
|
40
46
|
self.deployment = None
|
|
@@ -42,6 +48,7 @@ class Route53Stack(IStack, StandardizedSsmMixin):
|
|
|
42
48
|
self.hosted_zone = None
|
|
43
49
|
self.certificate = None
|
|
44
50
|
self.records = {}
|
|
51
|
+
self._distribution_cache = {} # Cache for reusing distributions
|
|
45
52
|
|
|
46
53
|
def build(self, stack_config: StackConfig, deployment: DeploymentConfig, workload: WorkloadConfig) -> None:
|
|
47
54
|
"""Build the Route53 stack"""
|
|
@@ -112,6 +119,232 @@ class Route53Stack(IStack, StandardizedSsmMixin):
|
|
|
112
119
|
return certificate
|
|
113
120
|
|
|
114
121
|
def _create_dns_records(self) -> None:
|
|
122
|
+
self._create_dns_records_old()
|
|
123
|
+
self._create_dns_records_new()
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _get_or_create_cloudfront_distribution(self, distribution_domain: str, distribution_id: str) -> cloudfront.Distribution:
|
|
127
|
+
"""Get or create a CloudFront distribution, reusing if already created"""
|
|
128
|
+
# Create a unique cache key from distribution domain and ID
|
|
129
|
+
cache_key = f"{distribution_domain}-{distribution_id}"
|
|
130
|
+
|
|
131
|
+
if cache_key not in self._distribution_cache:
|
|
132
|
+
# Create the distribution construct with a unique ID
|
|
133
|
+
unique_id = f"CF-{distribution_domain.replace('.', '-').replace('*', 'wildcard')}-{hash(cache_key) % 10000}"
|
|
134
|
+
distribution = cloudfront.Distribution.from_distribution_attributes(
|
|
135
|
+
self, unique_id,
|
|
136
|
+
domain_name=distribution_domain,
|
|
137
|
+
distribution_id=distribution_id
|
|
138
|
+
)
|
|
139
|
+
self._distribution_cache[cache_key] = distribution
|
|
140
|
+
logger.info(f"Created CloudFront distribution construct for {distribution_domain}")
|
|
141
|
+
|
|
142
|
+
return self._distribution_cache[cache_key]
|
|
143
|
+
|
|
144
|
+
def _create_dns_records_new(self) -> None:
|
|
145
|
+
"""Create DNS records based on configuration - generic implementation"""
|
|
146
|
+
|
|
147
|
+
missing_configurations = []
|
|
148
|
+
|
|
149
|
+
for record in self.route53_config.records:
|
|
150
|
+
record_name = record.get("name", "")
|
|
151
|
+
record_type = record.get("type", "")
|
|
152
|
+
|
|
153
|
+
if not record_name or not record_type:
|
|
154
|
+
message = f"Record missing name or type: {record}"
|
|
155
|
+
logger.warning(message)
|
|
156
|
+
missing_configurations.append(message)
|
|
157
|
+
continue
|
|
158
|
+
|
|
159
|
+
# Handle alias records
|
|
160
|
+
if "alias" in record:
|
|
161
|
+
alias_config = record["alias"]
|
|
162
|
+
target_type = alias_config.get("target_type", "")
|
|
163
|
+
target_value = alias_config.get("target_value", "")
|
|
164
|
+
hosted_zone_id = alias_config.get("hosted_zone_id", "")
|
|
165
|
+
|
|
166
|
+
unique_id = f"{record_name}-{record_type}"
|
|
167
|
+
# Handle SSM parameter references in target_value
|
|
168
|
+
target_value = self.resolve_ssm_value(self, target_value, unique_id=unique_id)
|
|
169
|
+
|
|
170
|
+
if not target_type or not target_value:
|
|
171
|
+
message = f"Alias record missing target_type or target_value: {record}"
|
|
172
|
+
logger.warning(message)
|
|
173
|
+
missing_configurations.append(message)
|
|
174
|
+
continue
|
|
175
|
+
|
|
176
|
+
# Create appropriate target based on type
|
|
177
|
+
alias_target = None
|
|
178
|
+
if target_type == "cloudfront":
|
|
179
|
+
# CloudFront distribution target
|
|
180
|
+
distribution_domain = target_value
|
|
181
|
+
distribution_id = alias_config.get("distribution_id", "")
|
|
182
|
+
if not distribution_id:
|
|
183
|
+
message = f"Alias record missing distribution_id: {record}"
|
|
184
|
+
logger.warning(message)
|
|
185
|
+
missing_configurations.append(message)
|
|
186
|
+
continue
|
|
187
|
+
|
|
188
|
+
# Get or create the distribution (reuses if already created)
|
|
189
|
+
distribution = self._get_or_create_cloudfront_distribution(distribution_domain, distribution_id)
|
|
190
|
+
alias_target = route53.RecordTarget.from_alias(
|
|
191
|
+
targets.CloudFrontTarget(distribution)
|
|
192
|
+
)
|
|
193
|
+
elif target_type == "loadbalancer" or target_type == "alb":
|
|
194
|
+
# Load Balancer target
|
|
195
|
+
alias_target = route53.RecordTarget.from_alias(
|
|
196
|
+
targets.LoadBalancerTarget(
|
|
197
|
+
elbv2.ApplicationLoadBalancer.from_load_balancer_attributes(
|
|
198
|
+
self, f"ALB-{record_name}",
|
|
199
|
+
load_balancer_dns_name=target_value,
|
|
200
|
+
load_balancer_canonical_hosted_zone_id=hosted_zone_id
|
|
201
|
+
)
|
|
202
|
+
)
|
|
203
|
+
)
|
|
204
|
+
elif target_type == "elbv2":
|
|
205
|
+
# Generic ELBv2 target
|
|
206
|
+
alias_target = route53.RecordTarget.from_alias(
|
|
207
|
+
targets.LoadBalancerTarget(
|
|
208
|
+
elbv2.ApplicationLoadBalancer.from_load_balancer_attributes(
|
|
209
|
+
self, f"ELB-{record_name}",
|
|
210
|
+
load_balancer_dns_name=target_value,
|
|
211
|
+
load_balancer_canonical_hosted_zone_id=hosted_zone_id
|
|
212
|
+
)
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
else:
|
|
216
|
+
message = f"Unsupported alias target type: {target_type}"
|
|
217
|
+
logger.warning(message)
|
|
218
|
+
missing_configurations.append(message)
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
# Create the alias record
|
|
222
|
+
route53.ARecord(
|
|
223
|
+
self,
|
|
224
|
+
f"AliasRecord-{record_name}-{record_type}",
|
|
225
|
+
zone=self.hosted_zone,
|
|
226
|
+
record_name=record_name,
|
|
227
|
+
target=alias_target,
|
|
228
|
+
ttl=cdk.Duration.seconds(record.get("ttl", 300))
|
|
229
|
+
) if record_type == "A" else route53.AaaaRecord(
|
|
230
|
+
self,
|
|
231
|
+
f"AliasRecord-{record_name}-{record_type}",
|
|
232
|
+
zone=self.hosted_zone,
|
|
233
|
+
record_name=record_name,
|
|
234
|
+
target=alias_target,
|
|
235
|
+
ttl=cdk.Duration.seconds(record.get("ttl", 300))
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Handle standard records with values
|
|
239
|
+
elif "values" in record:
|
|
240
|
+
values = record["values"]
|
|
241
|
+
if not isinstance(values, list):
|
|
242
|
+
values = [values]
|
|
243
|
+
|
|
244
|
+
# Handle SSM parameter references in values
|
|
245
|
+
processed_values = []
|
|
246
|
+
for value in values:
|
|
247
|
+
if "{{ssm:" in str(value) and "}}" in str(value):
|
|
248
|
+
# Extract SSM parameter path from template like {{ssm:/path/to/parameter}}
|
|
249
|
+
ssm_path = str(value).split("{{ssm:")[1].split("}}")[0]
|
|
250
|
+
resolved_value = self.get_ssm_imported_value(ssm_path)
|
|
251
|
+
processed_values.append(resolved_value)
|
|
252
|
+
else:
|
|
253
|
+
processed_values.append(value)
|
|
254
|
+
|
|
255
|
+
values = processed_values
|
|
256
|
+
ttl = record.get("ttl", 300)
|
|
257
|
+
|
|
258
|
+
# Create standard record based on type
|
|
259
|
+
if record_type == "A":
|
|
260
|
+
route53.ARecord(
|
|
261
|
+
self,
|
|
262
|
+
f"Record-{record_name}",
|
|
263
|
+
zone=self.hosted_zone,
|
|
264
|
+
record_name=record_name,
|
|
265
|
+
target=route53.RecordTarget.from_ip_addresses(*values),
|
|
266
|
+
ttl=cdk.Duration.seconds(ttl)
|
|
267
|
+
)
|
|
268
|
+
elif record_type == "AAAA":
|
|
269
|
+
route53.AaaaRecord(
|
|
270
|
+
self,
|
|
271
|
+
f"Record-{record_name}",
|
|
272
|
+
zone=self.hosted_zone,
|
|
273
|
+
record_name=record_name,
|
|
274
|
+
target=route53.RecordTarget.from_ip_addresses(*values),
|
|
275
|
+
ttl=cdk.Duration.seconds(ttl)
|
|
276
|
+
)
|
|
277
|
+
elif record_type == "CNAME":
|
|
278
|
+
route53.CnameRecord(
|
|
279
|
+
self,
|
|
280
|
+
f"Record-{record_name}",
|
|
281
|
+
zone=self.hosted_zone,
|
|
282
|
+
record_name=record_name,
|
|
283
|
+
domain_name=values[0], # CNAME only supports single value
|
|
284
|
+
ttl=cdk.Duration.seconds(ttl)
|
|
285
|
+
)
|
|
286
|
+
elif record_type == "MX":
|
|
287
|
+
# MX records need special handling for preference values
|
|
288
|
+
mx_targets = []
|
|
289
|
+
for value in values:
|
|
290
|
+
if isinstance(value, str) and " " in value:
|
|
291
|
+
preference, domain = value.split(" ", 1)
|
|
292
|
+
mx_targets.append(route53.MxRecordValue(
|
|
293
|
+
domain_name=domain.strip(),
|
|
294
|
+
preference=int(preference.strip())
|
|
295
|
+
))
|
|
296
|
+
else:
|
|
297
|
+
logger.warning(f"Invalid MX record format: {value}")
|
|
298
|
+
|
|
299
|
+
if mx_targets:
|
|
300
|
+
route53.MxRecord(
|
|
301
|
+
self,
|
|
302
|
+
f"Record-{record_name}",
|
|
303
|
+
zone=self.hosted_zone,
|
|
304
|
+
record_name=record_name,
|
|
305
|
+
values=mx_targets,
|
|
306
|
+
ttl=cdk.Duration.seconds(ttl)
|
|
307
|
+
)
|
|
308
|
+
elif record_type == "TXT":
|
|
309
|
+
route53.TxtRecord(
|
|
310
|
+
self,
|
|
311
|
+
f"Record-{record_name}",
|
|
312
|
+
zone=self.hosted_zone,
|
|
313
|
+
record_name=record_name,
|
|
314
|
+
values=values,
|
|
315
|
+
ttl=cdk.Duration.seconds(ttl)
|
|
316
|
+
)
|
|
317
|
+
elif record_type == "NS":
|
|
318
|
+
route53.NsRecord(
|
|
319
|
+
self,
|
|
320
|
+
f"Record-{record_name}",
|
|
321
|
+
zone=self.hosted_zone,
|
|
322
|
+
record_name=record_name,
|
|
323
|
+
values=values,
|
|
324
|
+
ttl=cdk.Duration.seconds(ttl)
|
|
325
|
+
)
|
|
326
|
+
else:
|
|
327
|
+
message = f"Unsupported record type: {record_type}"
|
|
328
|
+
logger.warning(message)
|
|
329
|
+
missing_configurations.append(message)
|
|
330
|
+
continue
|
|
331
|
+
|
|
332
|
+
else:
|
|
333
|
+
message = f"Record missing 'alias' or 'values' configuration: {record}"
|
|
334
|
+
logger.warning(message)
|
|
335
|
+
missing_configurations.append(message)
|
|
336
|
+
continue
|
|
337
|
+
|
|
338
|
+
if missing_configurations and len(missing_configurations) > 0:
|
|
339
|
+
# print all missing configurations
|
|
340
|
+
print("Missing configurations:")
|
|
341
|
+
for message in missing_configurations:
|
|
342
|
+
print(message)
|
|
343
|
+
|
|
344
|
+
messages = "\n".join(missing_configurations)
|
|
345
|
+
raise ValueError(f"Missing Configurations:\n{messages}")
|
|
346
|
+
|
|
347
|
+
def _create_dns_records_old(self) -> None:
|
|
115
348
|
"""Create DNS records based on configuration"""
|
|
116
349
|
# Create alias records
|
|
117
350
|
for alias_record in self.route53_config.aliases:
|
|
@@ -119,6 +352,12 @@ class Route53Stack(IStack, StandardizedSsmMixin):
|
|
|
119
352
|
target_type = alias_record.get("target_type", "")
|
|
120
353
|
target_value = alias_record.get("target_value", "")
|
|
121
354
|
|
|
355
|
+
# target value needs to handle SSM parameters
|
|
356
|
+
if "{{ssm:" in target_value and "}}" in target_value:
|
|
357
|
+
# Extract SSM parameter path from template like {{ssm:/path/to/parameter}}
|
|
358
|
+
ssm_path = target_value.split("{{ssm:")[1].split("}}")[0]
|
|
359
|
+
target_value = self.get_ssm_imported_value(ssm_path)
|
|
360
|
+
|
|
122
361
|
if not record_name or not target_type or not target_value:
|
|
123
362
|
continue
|
|
124
363
|
|
|
@@ -174,37 +413,4 @@ class Route53Stack(IStack, StandardizedSsmMixin):
|
|
|
174
413
|
def _add_outputs(self) -> None:
|
|
175
414
|
"""Add CloudFormation outputs for the Route53 resources"""
|
|
176
415
|
# Hosted Zone ID
|
|
177
|
-
|
|
178
|
-
cdk.CfnOutput(
|
|
179
|
-
self,
|
|
180
|
-
"HostedZoneId",
|
|
181
|
-
value=self.hosted_zone.hosted_zone_id,
|
|
182
|
-
export_name=f"{self.deployment.build_resource_name('hosted-zone')}-id"
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
# Hosted Zone Name Servers
|
|
186
|
-
if hasattr(self.hosted_zone, "name_servers") and self.hosted_zone.name_servers:
|
|
187
|
-
cdk.CfnOutput(
|
|
188
|
-
self,
|
|
189
|
-
"NameServers",
|
|
190
|
-
value=",".join(self.hosted_zone.name_servers),
|
|
191
|
-
export_name=f"{self.deployment.build_resource_name('hosted-zone')}-name-servers"
|
|
192
|
-
)
|
|
193
|
-
|
|
194
|
-
# Certificate ARN
|
|
195
|
-
if self.certificate:
|
|
196
|
-
cdk.CfnOutput(
|
|
197
|
-
self,
|
|
198
|
-
"CertificateArn",
|
|
199
|
-
value=self.certificate.certificate_arn,
|
|
200
|
-
export_name=f"{self.deployment.build_resource_name('certificate')}-arn"
|
|
201
|
-
)
|
|
202
|
-
|
|
203
|
-
# Record names
|
|
204
|
-
for name, record in self.records.items():
|
|
205
|
-
cdk.CfnOutput(
|
|
206
|
-
self,
|
|
207
|
-
f"Record-{name}",
|
|
208
|
-
value=name,
|
|
209
|
-
export_name=f"{self.deployment.build_resource_name('record')}-{name}"
|
|
210
|
-
)
|
|
416
|
+
return
|
|
@@ -72,7 +72,7 @@ class RumStack(IStack, StandardizedSsmMixin):
|
|
|
72
72
|
"cognito_identity_pool_id"
|
|
73
73
|
] = "/{{ORGANIZATION}}/{{ENVIRONMENT}}/cognito/user-pool/identity-pool-id"
|
|
74
74
|
|
|
75
|
-
self.
|
|
75
|
+
self.setup_ssm_integration(
|
|
76
76
|
scope=self,
|
|
77
77
|
config=rum_config,
|
|
78
78
|
resource_type="rum",
|
|
@@ -80,7 +80,7 @@ class RumStack(IStack, StandardizedSsmMixin):
|
|
|
80
80
|
)
|
|
81
81
|
|
|
82
82
|
# Process SSM imports using standardized method
|
|
83
|
-
self.
|
|
83
|
+
self.process_ssm_imports()
|
|
84
84
|
|
|
85
85
|
# Import or create Cognito resources
|
|
86
86
|
identity_pool_id, guest_role_arn = self._setup_cognito_integration()
|
|
@@ -341,7 +341,7 @@ class RumStack(IStack, StandardizedSsmMixin):
|
|
|
341
341
|
resource_values["user_pool_id"] = self.user_pool.user_pool_id
|
|
342
342
|
|
|
343
343
|
# Use enhanced SSM parameter export
|
|
344
|
-
exported_params = self.
|
|
344
|
+
exported_params = self.export_ssm_parameters(resource_values)
|
|
345
345
|
|
|
346
346
|
if exported_params:
|
|
347
347
|
logger.info(f"Exported {len(exported_params)} RUM parameters to SSM")
|
|
@@ -194,37 +194,7 @@ class SecurityGroupsStack(IStack, VPCProviderMixin):
|
|
|
194
194
|
description="Uptime Robot",
|
|
195
195
|
)
|
|
196
196
|
|
|
197
|
-
|
|
198
|
-
# Outputs (exports)
|
|
199
|
-
# =========================================================
|
|
200
|
-
cdk.CfnOutput(
|
|
201
|
-
self,
|
|
202
|
-
"WebFleetAlbSecurityGroupOut",
|
|
203
|
-
value=alb_sg.ref,
|
|
204
|
-
description="Web Fleet Application Load Balancer Security Group",
|
|
205
|
-
export_name=f"{self.deployment.environment}-{self.workload.name}-WebFleetAlbSecurityGroup",
|
|
206
|
-
)
|
|
207
|
-
cdk.CfnOutput(
|
|
208
|
-
self,
|
|
209
|
-
"WebFleetInstancesSecurityGroupOut",
|
|
210
|
-
value=web_fleet_sg.ref,
|
|
211
|
-
description="Web Fleet Instances Security Group",
|
|
212
|
-
export_name=f"{self.deployment.environment}-{self.workload.name}-WebFleetInstancesSecurityGroup",
|
|
213
|
-
)
|
|
214
|
-
cdk.CfnOutput(
|
|
215
|
-
self,
|
|
216
|
-
"MySqlDbSecurityGroupOut",
|
|
217
|
-
value=mysql_sg.ref,
|
|
218
|
-
description="MySql Security Group",
|
|
219
|
-
export_name=f"{self.deployment.environment}-{self.workload.name}-MySqlDbSecurityGroup",
|
|
220
|
-
)
|
|
221
|
-
cdk.CfnOutput(
|
|
222
|
-
self,
|
|
223
|
-
"WebMonitoringSecurityGroupOut",
|
|
224
|
-
value=monitoring_sg.ref,
|
|
225
|
-
description="Web Fleet Application Load Balancer Security Group",
|
|
226
|
-
export_name=f"{self.deployment.environment}-{self.workload.name}-WebMonitoringSecurityGroup",
|
|
227
|
-
)
|
|
197
|
+
|
|
228
198
|
|
|
229
199
|
# =========================================================
|
|
230
200
|
# SSM Parameter Store Exports
|
|
@@ -337,14 +337,7 @@ class SecurityGroupStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
|
|
|
337
337
|
|
|
338
338
|
def _export_cfn_outputs(self, sg_name: str) -> None:
|
|
339
339
|
"""Add CloudFormation outputs for the Security Group"""
|
|
340
|
-
|
|
341
|
-
# Security Group ID
|
|
342
|
-
cdk.CfnOutput(
|
|
343
|
-
self,
|
|
344
|
-
f"{sg_name}-id",
|
|
345
|
-
value=self.security_group.security_group_id,
|
|
346
|
-
export_name=f"{self.deployment.build_resource_name(sg_name)}-id",
|
|
347
|
-
)
|
|
340
|
+
return
|
|
348
341
|
|
|
349
342
|
def _export_ssm_parameters(self, sg_name: str) -> None:
|
|
350
343
|
"""Add SSM parameters for the Security Group"""
|
|
@@ -131,37 +131,4 @@ class SQSStack(IStack):
|
|
|
131
131
|
|
|
132
132
|
def _add_outputs(self) -> None:
|
|
133
133
|
"""Add CloudFormation outputs for the SQS queues"""
|
|
134
|
-
|
|
135
|
-
# Queue ARN
|
|
136
|
-
cdk.CfnOutput(
|
|
137
|
-
self,
|
|
138
|
-
f"{queue_name}-arn",
|
|
139
|
-
value=queue.queue_arn,
|
|
140
|
-
export_name=f"{self.deployment.build_resource_name(queue_name)}-arn"
|
|
141
|
-
)
|
|
142
|
-
|
|
143
|
-
# Queue URL
|
|
144
|
-
cdk.CfnOutput(
|
|
145
|
-
self,
|
|
146
|
-
f"{queue_name}-url",
|
|
147
|
-
value=queue.queue_url,
|
|
148
|
-
export_name=f"{self.deployment.build_resource_name(queue_name)}-url"
|
|
149
|
-
)
|
|
150
|
-
|
|
151
|
-
# Also add outputs for DLQs
|
|
152
|
-
for dlq_name, dlq in self.dead_letter_queues.items():
|
|
153
|
-
# DLQ ARN
|
|
154
|
-
cdk.CfnOutput(
|
|
155
|
-
self,
|
|
156
|
-
f"{dlq_name}-arn",
|
|
157
|
-
value=dlq.queue_arn,
|
|
158
|
-
export_name=f"{self.deployment.build_resource_name(dlq_name)}-arn"
|
|
159
|
-
)
|
|
160
|
-
|
|
161
|
-
# DLQ URL
|
|
162
|
-
cdk.CfnOutput(
|
|
163
|
-
self,
|
|
164
|
-
f"{dlq_name}-url",
|
|
165
|
-
value=dlq.queue_url,
|
|
166
|
-
export_name=f"{self.deployment.build_resource_name(dlq_name)}-url"
|
|
167
|
-
)
|
|
134
|
+
return
|
|
@@ -54,6 +54,11 @@ class StackStandards:
|
|
|
54
54
|
git_hash = GitUtilities.get_git_commit_hash()
|
|
55
55
|
if git_hash:
|
|
56
56
|
aws_cdk.Tags.of(scope).add("ApplicationGitHash", git_hash)
|
|
57
|
+
|
|
58
|
+
# Add CDK Factory version for tracking and debugging
|
|
59
|
+
from cdk_factory.version import __version__
|
|
60
|
+
aws_cdk.Tags.of(scope).add("CdkFactoryVersion", __version__)
|
|
61
|
+
|
|
57
62
|
aws_cdk.Tags.of(scope).add(
|
|
58
63
|
"DeploymentDateUTC", str(datetime.datetime.now(datetime.UTC))
|
|
59
64
|
)
|
|
@@ -74,7 +74,7 @@ class VpcStack(IStack, StandardizedSsmMixin):
|
|
|
74
74
|
vpc_name = deployment.build_resource_name(self.vpc_config.name)
|
|
75
75
|
|
|
76
76
|
# Setup standardized SSM integration
|
|
77
|
-
self.
|
|
77
|
+
self.setup_ssm_integration(
|
|
78
78
|
scope=self,
|
|
79
79
|
config=self.vpc_config,
|
|
80
80
|
resource_type="vpc",
|
|
@@ -84,7 +84,7 @@ class VpcStack(IStack, StandardizedSsmMixin):
|
|
|
84
84
|
)
|
|
85
85
|
|
|
86
86
|
# Process SSM imports using standardized method
|
|
87
|
-
self.
|
|
87
|
+
self.process_ssm_imports()
|
|
88
88
|
|
|
89
89
|
# Import any required resources from SSM
|
|
90
90
|
imported_resources = self.get_all_ssm_imports()
|
|
@@ -241,111 +241,8 @@ class VpcStack(IStack, StandardizedSsmMixin):
|
|
|
241
241
|
|
|
242
242
|
def _add_outputs(self, vpc_name: str) -> None:
|
|
243
243
|
"""Add CloudFormation outputs for the VPC"""
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
# VPC outputs
|
|
248
|
-
cdk.CfnOutput(
|
|
249
|
-
self,
|
|
250
|
-
f"{vpc_name}-VpcId",
|
|
251
|
-
value=self.vpc.vpc_id,
|
|
252
|
-
description=f"VPC ID for {vpc_name}",
|
|
253
|
-
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-vpc-id",
|
|
254
|
-
)
|
|
255
|
-
|
|
256
|
-
# Subnet outputs
|
|
257
|
-
public_subnet_ids = [subnet.subnet_id for subnet in self.vpc.public_subnets]
|
|
258
|
-
if public_subnet_ids:
|
|
259
|
-
cdk.CfnOutput(
|
|
260
|
-
self,
|
|
261
|
-
f"{vpc_name}-PublicSubnetIds",
|
|
262
|
-
value=",".join(public_subnet_ids),
|
|
263
|
-
description=f"Public subnet IDs for {vpc_name}",
|
|
264
|
-
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-public-subnet-ids",
|
|
265
|
-
)
|
|
266
|
-
|
|
267
|
-
private_subnet_ids = [subnet.subnet_id for subnet in self.vpc.private_subnets]
|
|
268
|
-
if private_subnet_ids:
|
|
269
|
-
cdk.CfnOutput(
|
|
270
|
-
self,
|
|
271
|
-
f"{vpc_name}-PrivateSubnetIds",
|
|
272
|
-
value=",".join(private_subnet_ids),
|
|
273
|
-
description=f"Private subnet IDs for {vpc_name}",
|
|
274
|
-
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-private-subnet-ids",
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
isolated_subnet_ids = [subnet.subnet_id for subnet in self.vpc.isolated_subnets]
|
|
278
|
-
if isolated_subnet_ids:
|
|
279
|
-
cdk.CfnOutput(
|
|
280
|
-
self,
|
|
281
|
-
f"{vpc_name}-IsolatedSubnetIds",
|
|
282
|
-
value=",".join(isolated_subnet_ids),
|
|
283
|
-
description=f"Isolated subnet IDs for {vpc_name}",
|
|
284
|
-
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-isolated-subnet-ids",
|
|
285
|
-
)
|
|
286
|
-
|
|
287
|
-
# Route table outputs - simplified to avoid route table access issues
|
|
288
|
-
# Skip route table outputs for now as they're causing CDK API issues
|
|
289
|
-
# public_route_table_ids = []
|
|
290
|
-
# if self.vpc.public_subnets:
|
|
291
|
-
# for subnet in self.vpc.public_subnets:
|
|
292
|
-
# # Access route table through the subnet's route table association
|
|
293
|
-
# for association in subnet.node.children:
|
|
294
|
-
# if hasattr(association, 'route_table_id') and association.route_table_id:
|
|
295
|
-
# public_route_table_ids.append(association.route_table_id)
|
|
296
|
-
#
|
|
297
|
-
# if public_route_table_ids:
|
|
298
|
-
# cdk.CfnOutput(
|
|
299
|
-
# self,
|
|
300
|
-
# f"{vpc_name}-PublicRouteTableIds",
|
|
301
|
-
# value=",".join(public_route_table_ids),
|
|
302
|
-
# description=f"Public route table IDs for {vpc_name}",
|
|
303
|
-
# export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-public-route-table-ids",
|
|
304
|
-
# )
|
|
305
|
-
#
|
|
306
|
-
# private_route_table_ids = []
|
|
307
|
-
# if self.vpc.private_subnets:
|
|
308
|
-
# for subnet in self.vpc.private_subnets:
|
|
309
|
-
# # Access route table through the subnet's route table association
|
|
310
|
-
# for association in subnet.node.children:
|
|
311
|
-
# if hasattr(association, 'route_table_id') and association.route_table_id:
|
|
312
|
-
# private_route_table_ids.append(association.route_table_id)
|
|
313
|
-
#
|
|
314
|
-
# if private_route_table_ids:
|
|
315
|
-
# cdk.CfnOutput(
|
|
316
|
-
# self,
|
|
317
|
-
# f"{vpc_name}-PrivateRouteTableIds",
|
|
318
|
-
# value=",".join(private_route_table_ids),
|
|
319
|
-
# description=f"Private route table IDs for {vpc_name}",
|
|
320
|
-
# export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-private-route-table-ids",
|
|
321
|
-
# )
|
|
322
|
-
|
|
323
|
-
# Internet Gateway output
|
|
324
|
-
if hasattr(self.vpc, 'internet_gateway_id') and self.vpc.internet_gateway_id:
|
|
325
|
-
cdk.CfnOutput(
|
|
326
|
-
self,
|
|
327
|
-
f"{vpc_name}-InternetGatewayId",
|
|
328
|
-
value=self.vpc.internet_gateway_id,
|
|
329
|
-
description=f"Internet Gateway ID for {vpc_name}",
|
|
330
|
-
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-internet-gateway-id",
|
|
331
|
-
)
|
|
332
|
-
|
|
333
|
-
# NAT Gateway outputs - simplified to avoid None values
|
|
334
|
-
nat_gateway_ids = []
|
|
335
|
-
for subnet in self.vpc.public_subnets:
|
|
336
|
-
if hasattr(subnet, 'node') and subnet.node:
|
|
337
|
-
for child in subnet.node.children:
|
|
338
|
-
if hasattr(child, 'nat_gateway_id') and child.nat_gateway_id:
|
|
339
|
-
nat_gateway_ids.append(child.nat_gateway_id)
|
|
340
|
-
|
|
341
|
-
if nat_gateway_ids:
|
|
342
|
-
cdk.CfnOutput(
|
|
343
|
-
self,
|
|
344
|
-
f"{vpc_name}-NatGatewayIds",
|
|
345
|
-
value=",".join(nat_gateway_ids),
|
|
346
|
-
description=f"NAT Gateway IDs for {vpc_name}",
|
|
347
|
-
export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-nat-gateway-ids",
|
|
348
|
-
)
|
|
244
|
+
return
|
|
245
|
+
|
|
349
246
|
|
|
350
247
|
def _export_ssm_parameters(self) -> None:
|
|
351
248
|
"""Export SSM parameters using standardized approach"""
|
|
@@ -399,7 +296,7 @@ class VpcStack(IStack, StandardizedSsmMixin):
|
|
|
399
296
|
resource_values["internet_gateway_id"] = self.vpc.internet_gateway_id
|
|
400
297
|
|
|
401
298
|
# Export using standardized SSM mixin
|
|
402
|
-
exported_params = self.
|
|
299
|
+
exported_params = self.export_ssm_parameters(resource_values)
|
|
403
300
|
|
|
404
301
|
logger.info(f"Exported SSM parameters: {exported_params}")
|
|
405
302
|
|
|
@@ -431,7 +328,7 @@ class VpcStack(IStack, StandardizedSsmMixin):
|
|
|
431
328
|
# Backward compatibility methods
|
|
432
329
|
def auto_export_resources(self, resource_values: Dict[str, Any], context: Dict[str, Any] = None) -> Dict[str, str]:
|
|
433
330
|
"""Backward compatibility method for existing modules."""
|
|
434
|
-
return self.
|
|
331
|
+
return self.export_ssm_parameters(resource_values)
|
|
435
332
|
|
|
436
333
|
def auto_import_resources(self, context: Dict[str, Any] = None) -> Dict[str, Any]:
|
|
437
334
|
"""Backward compatibility method for existing modules."""
|
|
@@ -113,12 +113,16 @@ class StaticWebSiteStack(IStack):
|
|
|
113
113
|
self, stack_config: StackConfig, workload: WorkloadConfig
|
|
114
114
|
) -> str:
|
|
115
115
|
source = stack_config.dictionary.get("src", {}).get("path")
|
|
116
|
+
if not source:
|
|
117
|
+
raise ValueError("Source path is required for static website stack")
|
|
116
118
|
for base in workload.paths:
|
|
117
|
-
|
|
119
|
+
if base is None:
|
|
120
|
+
continue
|
|
121
|
+
candidate = Path(os.path.join(str(Path(base)), source)).resolve()
|
|
118
122
|
|
|
119
123
|
if candidate.exists():
|
|
120
124
|
return str(candidate)
|
|
121
|
-
raise ValueError(f"Could not find the source path: {source}")
|
|
125
|
+
raise ValueError(f"Could not find the source path for static site: {source}")
|
|
122
126
|
|
|
123
127
|
def __setup_cloudfront_distribution(
|
|
124
128
|
self,
|
|
@@ -225,7 +229,7 @@ class StaticWebSiteStack(IStack):
|
|
|
225
229
|
bucket: The S3 bucket
|
|
226
230
|
cloudfront_distribution: The CloudFront distribution construct
|
|
227
231
|
"""
|
|
228
|
-
ssm_exports = stack_config.dictionary.get("
|
|
232
|
+
ssm_exports = stack_config.dictionary.get("ssm", {}).get("exports", {})
|
|
229
233
|
|
|
230
234
|
if not ssm_exports:
|
|
231
235
|
logger.debug("No SSM exports configured for this stack")
|
|
@@ -555,7 +555,7 @@ class ApiGatewayIntegrationUtility:
|
|
|
555
555
|
api_gateway_config["ssm"]["imports"] = {}
|
|
556
556
|
api_gateway_config["ssm"]["imports"]["user_pool_arn"] = "/{{ORGANIZATION}}/{{ENVIRONMENT}}/cognito/user-pool/arn"
|
|
557
557
|
|
|
558
|
-
ssm_mixin.
|
|
558
|
+
ssm_mixin.setup_ssm_integration(
|
|
559
559
|
scope=self.scope,
|
|
560
560
|
config=api_gateway_config,
|
|
561
561
|
resource_type="cognito",
|
|
@@ -882,7 +882,7 @@ class ApiGatewayIntegrationUtility:
|
|
|
882
882
|
|
|
883
883
|
# Setup enhanced SSM integration for auto-import
|
|
884
884
|
# Use consistent resource name for cross-stack compatibility
|
|
885
|
-
ssm_mixin.
|
|
885
|
+
ssm_mixin.setup_ssm_integration(
|
|
886
886
|
scope=self.scope,
|
|
887
887
|
config=api_gateway_config,
|
|
888
888
|
resource_type="api-gateway",
|
|
@@ -173,9 +173,9 @@ class EnvironmentServices:
|
|
|
173
173
|
environment = {}
|
|
174
174
|
# more verbose
|
|
175
175
|
environment["WORKLOAD_NAME"] = deployment.workload.get("name", "NA")
|
|
176
|
-
environment["ENVIRONMENT_NAME"] = deployment.environment
|
|
176
|
+
environment["ENVIRONMENT_NAME"] = deployment.workload.get("environment", deployment.environment)
|
|
177
177
|
environment["DEPLOYMENT_NAME"] = deployment.name
|
|
178
|
-
environment["ENVIRONMENT"] = deployment.environment
|
|
178
|
+
environment["ENVIRONMENT"] = deployment.workload.get("environment", deployment.environment)
|
|
179
179
|
environment["PIPELINE"] = deployment.pipeline.get("name", "NA")
|
|
180
180
|
environment["ACCOUNT"] = deployment.account
|
|
181
181
|
environment["DEPLOYMENT"] = deployment.name
|
cdk_factory/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.20.0"
|