cdk-factory 0.18.21__py3-none-any.whl → 0.19.13__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.
Files changed (24) hide show
  1. cdk_factory/configurations/resources/lambda_edge.py +18 -4
  2. cdk_factory/configurations/resources/rds.py +1 -1
  3. cdk_factory/configurations/resources/s3.py +9 -1
  4. cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py +1 -1
  5. cdk_factory/stack_library/acm/acm_stack.py +5 -15
  6. cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +51 -32
  7. cdk_factory/stack_library/cloudfront/cloudfront_stack.py +48 -23
  8. cdk_factory/stack_library/code_artifact/code_artifact_stack.py +3 -25
  9. cdk_factory/stack_library/ecs/ecs_service_stack.py +38 -25
  10. cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +119 -28
  11. cdk_factory/stack_library/load_balancer/load_balancer_stack.py +2 -59
  12. cdk_factory/stack_library/rds/rds_stack.py +1 -25
  13. cdk_factory/stack_library/route53/route53_stack.py +1 -34
  14. cdk_factory/stack_library/security_group/security_group_full_stack.py +1 -31
  15. cdk_factory/stack_library/security_group/security_group_stack.py +1 -8
  16. cdk_factory/stack_library/simple_queue_service/sqs_stack.py +1 -34
  17. cdk_factory/stack_library/vpc/vpc_stack.py +2 -109
  18. cdk_factory/stack_library/websites/static_website_stack.py +6 -2
  19. cdk_factory/version.py +1 -1
  20. {cdk_factory-0.18.21.dist-info → cdk_factory-0.19.13.dist-info}/METADATA +1 -1
  21. {cdk_factory-0.18.21.dist-info → cdk_factory-0.19.13.dist-info}/RECORD +24 -24
  22. {cdk_factory-0.18.21.dist-info → cdk_factory-0.19.13.dist-info}/WHEEL +0 -0
  23. {cdk_factory-0.18.21.dist-info → cdk_factory-0.19.13.dist-info}/entry_points.txt +0 -0
  24. {cdk_factory-0.18.21.dist-info → cdk_factory-0.19.13.dist-info}/licenses/LICENSE +0 -0
@@ -49,16 +49,30 @@ class LambdaEdgeConfig(EnhancedBaseConfig):
49
49
 
50
50
  @property
51
51
  def timeout(self) -> int:
52
- """Timeout in seconds (max 5 for origin-request)"""
52
+ """Timeout in seconds
53
+ viewer-request: 5s
54
+ viewer-response: 5s
55
+ ---
56
+ origin-request: 30s
57
+ origin-response: 30s
58
+
59
+
60
+ """
53
61
  timeout = int(self._config.get("timeout", 5))
54
- if timeout > 5:
55
- raise ValueError("Lambda@Edge origin-request timeout cannot exceed 5 seconds")
62
+
63
+ event_type = self.event_type
64
+ if event_type == "viewer-request" or event_type == "viewer-response":
65
+ if timeout > 5:
66
+ raise ValueError("Lambda@Edge viewer timeout cannot exceed 5 seconds. Value was set to {}".format(timeout))
67
+ else:
68
+ if timeout > 30:
69
+ raise ValueError("Lambda@Edge origin timeout cannot exceed 30 seconds. Value was set to {}".format(timeout))
56
70
  return timeout
57
71
 
58
72
  @property
59
73
  def code_path(self) -> str:
60
74
  """Path to Lambda function code directory"""
61
- return self._config.get("code_path", "./lambdas/edge/ip_gate")
75
+ return self._config.get("code_path", "./lambdas/cloudfront/ip_gate")
62
76
 
63
77
  @property
64
78
  def environment(self) -> Dict[str, str]:
@@ -29,7 +29,7 @@ class RdsConfig(EnhancedBaseConfig):
29
29
  @property
30
30
  def name(self) -> str:
31
31
  """RDS instance name"""
32
- return self.__config.get("name", "database")
32
+ return self.__config.get("name", self.database_name)
33
33
 
34
34
  @property
35
35
  def identifier(self) -> str:
@@ -158,7 +158,15 @@ class S3BucketConfig(EnhancedBaseConfig):
158
158
  value = self.__config.get("block_public_access")
159
159
 
160
160
  if value and isinstance(value, str):
161
- if value.lower() == "block_acls":
161
+ if value.lower() == "disabled":
162
+ # For public website hosting, disable block public access
163
+ return s3.BlockPublicAccess(
164
+ block_public_acls=False,
165
+ block_public_policy=False,
166
+ ignore_public_acls=False,
167
+ restrict_public_buckets=False
168
+ )
169
+ elif value.lower() == "block_acls":
162
170
  return s3.BlockPublicAccess.BLOCK_ACLS
163
171
  # elif value.lower() == "block_public_acls":
164
172
  # return s3.BlockPublicAccess.block_public_acls
@@ -558,7 +558,7 @@ class CloudFrontDistributionConstruct(Construct):
558
558
  """
559
559
  bucket_policy = s3.BucketPolicy(
560
560
  self,
561
- id=f"CloudFrontBucketPolicy-{self.source_bucket.bucket_name}",
561
+ id="CloudFrontBucketPolicy",
562
562
  bucket=self.source_bucket,
563
563
  )
564
564
 
@@ -25,6 +25,7 @@ logger = Logger(service="AcmStack")
25
25
 
26
26
  @register_stack("acm_stack")
27
27
  @register_stack("certificate_stack")
28
+ @register_stack("certificate_library_module")
28
29
  class AcmStack(IStack, StandardizedSsmMixin):
29
30
  """
30
31
  Reusable stack for AWS Certificate Manager.
@@ -152,18 +153,7 @@ class AcmStack(IStack, StandardizedSsmMixin):
152
153
 
153
154
  def _add_outputs(self, cert_name: str) -> None:
154
155
  """Add CloudFormation outputs"""
155
- cdk.CfnOutput(
156
- self,
157
- "CertificateArn",
158
- value=self.certificate.certificate_arn,
159
- description=f"Certificate ARN for {self.acm_config.domain_name}",
160
- export_name=f"{cert_name}-arn",
161
- )
162
-
163
- cdk.CfnOutput(
164
- self,
165
- "DomainName",
166
- value=self.acm_config.domain_name,
167
- description="Primary domain name for the certificate",
168
- export_name=f"{cert_name}-domain",
169
- )
156
+
157
+ return
158
+
159
+
@@ -13,7 +13,7 @@ from aws_cdk import aws_cloudwatch as cloudwatch
13
13
  from aws_cdk import aws_iam as iam
14
14
  from aws_cdk import aws_ssm as ssm
15
15
  from aws_cdk import aws_ecs as ecs
16
- from aws_cdk import Duration, Stack
16
+ from aws_cdk import Duration, Stack, CfnUpdatePolicy
17
17
  from aws_lambda_powertools import Logger
18
18
  from constructs import Construct
19
19
 
@@ -539,43 +539,62 @@ class AutoScalingStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
539
539
 
540
540
  if not instance_refresh_config.get("enabled", False):
541
541
  return
542
+
543
+ logger.warning("Instance refresh is not supported in this version of the CDK")
544
+ return
542
545
 
543
546
  # Get the CloudFormation ASG resource
544
547
  cfn_asg = asg.node.default_child
545
548
 
546
- # Configure instance refresh properties
547
- refresh_props = {}
549
+ # Configure instance refresh using CloudFormation UpdatePolicy
550
+ # UpdatePolicy is added at the resource level, not as a property
551
+ update_policy = {
552
+ "AutoScalingRollingUpdate": {
553
+ "PauseTime": "PT300S", # 5 minutes pause
554
+ "MinInstancesInService": "1",
555
+ "MaxBatchSize": "1",
556
+ "WaitOnResourceSignals": True,
557
+ "SuspendProcesses": [
558
+ "HealthCheck",
559
+ "ReplaceUnhealthy",
560
+ "AZRebalance",
561
+ "AlarmNotification",
562
+ "ScheduledActions"
563
+ ]
564
+ }
565
+ }
548
566
 
549
- if "min_healthy_percentage" in instance_refresh_config:
550
- refresh_props["min_healthy_percentage"] = instance_refresh_config["min_healthy_percentage"]
551
-
552
- if "instance_warmup" in instance_refresh_config:
553
- refresh_props["instance_warmup"] = instance_refresh_config["instance_warmup"]
554
-
555
- if "preferences" in instance_refresh_config:
556
- preferences = instance_refresh_config["preferences"]
557
-
558
- # Build preferences property
559
- pref_props = {}
560
-
561
- if "skip_matching" in preferences:
562
- pref_props["skip_matching"] = preferences["skip_matching"]
563
-
564
- if "auto_rollback" in preferences:
565
- pref_props["auto_rollback"] = preferences["auto_rollback"]
566
-
567
- if "instance_warmup" in preferences:
568
- pref_props["instance_warmup"] = preferences["instance_warmup"]
569
-
570
- if "min_healthy_percentage" in preferences:
571
- pref_props["min_healthy_percentage"] = preferences["min_healthy_percentage"]
572
-
573
- if pref_props:
574
- refresh_props["preferences"] = pref_props
567
+ # # Apply instance refresh using CloudFormation's cfn_options.update_policy
568
+ # cfn_asg.cfn_options.update_policy = cdk.CfnUpdatePolicy.from_rolling_update(
569
+ # pause_time=cdk.Duration.seconds(300),
570
+ # min_instances_in_service=1,
571
+ # max_batch_size=1,
572
+ # wait_on_resource_signals=True
573
+ # )
574
+
575
+ # Grab the L1 to attach UpdatePolicy.InstanceRefresh
576
+ cfn_asg: autoscaling.CfnAutoScalingGroup = asg.node.default_child
577
+
578
+ # cfn_asg.cfn_options.update_policy = CfnUpdatePolicy.from_auto_scaling_instance_refresh(
579
+ # # Triggers tell CFN *what* changes should start a refresh
580
+ # triggers=[CfnUpdatePolicy.InstanceRefreshTrigger.LAUNCH_TEMPLATE],
581
+ # preferences=CfnUpdatePolicy.InstanceRefreshPreferences(
582
+ # # warmup is like “grace” before counting a new instance healthy
583
+ # instance_warmup=Duration.minutes(5),
584
+ # # how aggressive the refresh is; 90 keeps capacity high
585
+ # min_healthy_percentage=90,
586
+ # # skip instances that already match the new LT (fast when only userdata/env tweaked)
587
+ # skip_matching=True,
588
+ # # optional: put instances in Standby first; default is rolling terminate/launch
589
+ # # standby_instances=CfnUpdatePolicy.StandbyInstances.TERMINATE,
590
+ # # checkpoint_percentages=[25, 50, 75], # optional: progressive checkpoints
591
+ # # checkpoint_delay=Duration.minutes(2), # optional delay at checkpoints
592
+ # ),
593
+ # )
594
+ logger.info(f"Configured instance refresh via CDK CfnUpdatePolicy")
575
595
 
576
- if refresh_props:
577
- cfn_asg.add_property_override("InstanceRefresh", refresh_props)
578
- logger.info(f"Configured instance refresh: {refresh_props}")
596
+ # Note: This provides rolling update functionality similar to instance refresh
597
+ # For true instance refresh with preferences, we would need CDK v2.80+ or custom CloudFormation
579
598
 
580
599
 
581
600
  # Backward compatibility alias
@@ -14,6 +14,7 @@ from aws_cdk import (
14
14
  aws_cloudfront_origins as origins,
15
15
  aws_certificatemanager as acm,
16
16
  aws_route53 as route53,
17
+ aws_s3 as s3,
17
18
  aws_lambda as _lambda,
18
19
  aws_ssm as ssm,
19
20
  CfnOutput,
@@ -144,7 +145,7 @@ class CloudFrontStack(IStack):
144
145
  return
145
146
 
146
147
  # Check if certificate ARN is provided
147
- cert_arn = cert_config.get("arn")
148
+ cert_arn = self.resolve_ssm_value(self, cert_config.get("arn"), "CertificateARN")
148
149
  if cert_arn:
149
150
  self.certificate = acm.Certificate.from_certificate_arn(
150
151
  self, "Certificate", certificate_arn=cert_arn
@@ -172,14 +173,14 @@ class CloudFrontStack(IStack):
172
173
  "CloudFront certificates must be created in us-east-1"
173
174
  )
174
175
  return
175
-
176
+
176
177
  # Create the certificate
177
178
  # Get hosted zone from SSM imports
178
179
  hosted_zone_id = cert_config.get("hosted_zone_id")
179
180
  hosted_zone = route53.HostedZone.from_hosted_zone_id(
180
181
  self, "HostedZone", hosted_zone_id
181
182
  )
182
-
183
+
183
184
  self.certificate = acm.Certificate(
184
185
  self,
185
186
  "Certificate",
@@ -222,27 +223,29 @@ class CloudFrontStack(IStack):
222
223
 
223
224
  def _create_custom_origin(self, config: Dict[str, Any]) -> cloudfront.IOrigin:
224
225
  """Create custom origin (ALB, API Gateway, etc.)"""
225
- domain_name = config.get("domain_name")
226
+ domain_name = self.resolve_ssm_value(
227
+ self, config.get("domain_name"), config.get("domain_name")
228
+ )
226
229
  origin_id = config.get("id")
227
230
 
228
231
  if not domain_name:
229
232
  raise ValueError("domain_name is required for custom origin")
230
233
 
231
- # Check if domain name is a placeholder from ssm_imports
232
- if domain_name.startswith("{{") and domain_name.endswith("}}"):
233
- placeholder_key = domain_name[2:-2] # Remove {{ and }}
234
- if placeholder_key in self.ssm_imported_values:
235
- domain_name = self.ssm_imported_values[placeholder_key]
236
- logger.info(f"Resolved domain from SSM import: {placeholder_key}")
237
- else:
238
- logger.warning(f"Placeholder {domain_name} not found in SSM imports")
239
-
240
- # Legacy support: Check if domain name is an SSM parameter reference
241
- elif domain_name.startswith("{{ssm:") and domain_name.endswith("}}"):
242
- # Extract SSM parameter name
243
- ssm_param = domain_name[6:-2] # Remove {{ssm: and }}
244
- domain_name = ssm.StringParameter.value_from_lookup(self, ssm_param)
245
- logger.info(f"Resolved domain from SSM lookup {ssm_param}: {domain_name}")
234
+ # # Check if domain name is a placeholder from ssm_imports
235
+ # if domain_name.startswith("{{") and domain_name.endswith("}}"):
236
+ # placeholder_key = domain_name[2:-2] # Remove {{ and }}
237
+ # if placeholder_key in self.ssm_imported_values:
238
+ # domain_name = self.ssm_imported_values[placeholder_key]
239
+ # logger.info(f"Resolved domain from SSM import: {placeholder_key}")
240
+ # else:
241
+ # logger.warning(f"Placeholder {domain_name} not found in SSM imports")
242
+
243
+ # # Legacy support: Check if domain name is an SSM parameter reference
244
+ # elif domain_name.startswith("{{ssm:") and domain_name.endswith("}}"):
245
+ # # Extract SSM parameter name
246
+ # ssm_param = domain_name[6:-2] # Remove {{ssm: and }}
247
+ # domain_name = ssm.StringParameter.value_from_lookup(self, ssm_param)
248
+ # logger.info(f"Resolved domain from SSM lookup {ssm_param}: {domain_name}")
246
249
 
247
250
  # Build custom headers (e.g., X-Origin-Secret)
248
251
  custom_headers = {}
@@ -296,12 +299,34 @@ class CloudFrontStack(IStack):
296
299
 
297
300
  def _create_s3_origin(self, config: Dict[str, Any]) -> cloudfront.IOrigin:
298
301
  """Create S3 origin"""
299
- # S3 origin implementation
300
- # This would use origins.S3Origin
301
- raise NotImplementedError(
302
- "S3 origin support - use existing static_website_stack for S3"
302
+ bucket_name = self.resolve_ssm_value(
303
+ self, config.get("bucket_name"), config.get("bucket_name")
303
304
  )
304
305
 
306
+ origin_path = config.get("origin_path", "")
307
+
308
+ if not bucket_name:
309
+ raise ValueError("S3 origin requires 'bucket_name' configuration")
310
+
311
+ # For S3 origins, we need to import the bucket by name
312
+ bucket = s3.Bucket.from_bucket_name(
313
+ self,
314
+ id=f"S3OriginBucket-{config.get('id', 'unknown')}",
315
+ bucket_name=bucket_name,
316
+ )
317
+
318
+ # Create S3 origin with OAC (Origin Access Control) for security
319
+ origin = origins.S3BucketOrigin.with_origin_access_control(
320
+ bucket,
321
+ origin_path=origin_path,
322
+ origin_access_levels=[
323
+ cloudfront.AccessLevel.READ,
324
+ cloudfront.AccessLevel.LIST,
325
+ ],
326
+ )
327
+
328
+ return origin
329
+
305
330
  def _create_distribution(self) -> None:
306
331
  """Create CloudFront distribution"""
307
332
 
@@ -140,32 +140,10 @@ class CodeArtifactStack(IStack, StandardizedSsmMixin):
140
140
 
141
141
  def _add_outputs(self) -> None:
142
142
  """Add CloudFormation outputs for the CodeArtifact resources"""
143
+
144
+
143
145
  # Domain outputs
144
146
  if self.domain:
145
147
  domain_name = self.code_artifact_config.domain_name
146
148
 
147
- # Domain ARN
148
- cdk.CfnOutput(
149
- self,
150
- f"{domain_name}-domain-arn",
151
- value=self.domain.attr_arn,
152
- export_name=f"{self.deployment.build_resource_name(domain_name)}-domain-arn"
153
- )
154
-
155
- # Domain URL
156
- cdk.CfnOutput(
157
- self,
158
- f"{domain_name}-domain-url",
159
- value=f"https://{self.code_artifact_config.account}.d.codeartifact.{self.code_artifact_config.region}.amazonaws.com/",
160
- export_name=f"{self.deployment.build_resource_name(domain_name)}-domain-url"
161
- )
162
-
163
- # Repository outputs
164
- for repo_name, repo in self.repositories.items():
165
- # Repository ARN
166
- cdk.CfnOutput(
167
- self,
168
- f"{repo_name}-repo-arn",
169
- value=repo.attr_arn,
170
- export_name=f"{self.deployment.build_resource_name(repo_name)}-repo-arn"
171
- )
149
+
@@ -101,6 +101,7 @@ class EcsServiceStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
101
101
 
102
102
  # Add outputs
103
103
  self._add_outputs(service_name)
104
+ self._export_to_ssm(service_name)
104
105
 
105
106
  def _load_vpc(self) -> None:
106
107
  """Load VPC using the centralized VPC provider mixin."""
@@ -225,6 +226,9 @@ class EcsServiceStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
225
226
  "CloudWatchAgentServerPolicy"
226
227
  )
227
228
  )
229
+
230
+ # add any custom policies
231
+ self._add_custom_task_policies(task_role)
228
232
 
229
233
  # Create task definition based on launch type
230
234
  if self.ecs_config.launch_type == "EC2":
@@ -256,6 +260,37 @@ class EcsServiceStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
256
260
  # Add containers
257
261
  self._add_containers_to_task()
258
262
 
263
+ def _add_custom_task_policies(self, task_role: iam.Role) -> None:
264
+ """
265
+ Add custom task policies to the task definition.
266
+ """
267
+ for policy in self.ecs_config.task_definition.get("policies", []):
268
+
269
+ effect = policy.get("effect", "Allow")
270
+ action = policy.get("action", None)
271
+ actions = policy.get("actions", [])
272
+ if action:
273
+ actions.append(action)
274
+ resources = policy.get("resources", [])
275
+ resource = policy.get("resource", None)
276
+ if resource:
277
+ resources.append(resource)
278
+
279
+ if effect == "Allow" and actions:
280
+ effect = iam.Effect.ALLOW
281
+ if effect == "Deny" and actions:
282
+ effect = iam.Effect.DENY
283
+
284
+ sid = policy.get("sid", None)
285
+ task_role.add_to_policy(
286
+ iam.PolicyStatement(
287
+ effect=effect,
288
+ actions=actions,
289
+ resources=resources,
290
+ sid=sid,
291
+ )
292
+ )
293
+
259
294
  def _add_volumes_to_task(self) -> None:
260
295
  """
261
296
  Add volumes to the task definition.
@@ -603,35 +638,13 @@ class EcsServiceStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
603
638
  scale_out_cooldown=cdk.Duration.seconds(60),
604
639
  )
605
640
 
641
+
642
+
606
643
  def _add_outputs(self, service_name: str) -> None:
607
644
  """Add CloudFormation outputs"""
645
+ return
608
646
 
609
- # Service name output
610
- cdk.CfnOutput(
611
- self,
612
- "ServiceName",
613
- value=self.service.service_name,
614
- description=f"ECS Service Name: {service_name}",
615
- )
616
-
617
- # Service ARN output
618
- cdk.CfnOutput(
619
- self,
620
- "ServiceArn",
621
- value=self.service.service_arn,
622
- description=f"ECS Service ARN: {service_name}",
623
- )
624
-
625
- # Cluster name output
626
- cdk.CfnOutput(
627
- self,
628
- "ClusterName",
629
- value=self.cluster.cluster_name,
630
- description=f"ECS Cluster Name for {service_name}",
631
- )
632
647
 
633
- # Export to SSM if configured
634
- self._export_to_ssm(service_name)
635
648
 
636
649
  def _export_to_ssm(self, service_name: str) -> None:
637
650
  """Export resource ARNs and names to SSM Parameter Store"""
@@ -53,6 +53,8 @@ class LambdaEdgeStack(IStack, StandardizedSsmMixin):
53
53
  self.workload: Optional[WorkloadConfig] = None
54
54
  self.function: Optional[_lambda.Function] = None
55
55
  self.function_version: Optional[_lambda.Version] = None
56
+ # Cache for resolved environment variables to prevent duplicate construct creation
57
+ self._resolved_env_cache: Optional[Dict[str, str]] = None
56
58
 
57
59
  def build(
58
60
  self,
@@ -98,15 +100,38 @@ class LambdaEdgeStack(IStack, StandardizedSsmMixin):
98
100
  # Create version (required for Lambda@Edge)
99
101
  self._create_function_version(function_name)
100
102
 
103
+ # Configure edge log retention for regional logs
104
+ self._configure_edge_log_retention(function_name)
105
+
101
106
  # Add outputs
102
107
  self._add_outputs(function_name)
103
108
 
109
+ def _sanitize_construct_name(self, name: str) -> str:
110
+ """
111
+ Create a deterministic, valid CDK construct name from any string.
112
+ Replaces non-alphanumeric characters with dashes and limits length.
113
+ """
114
+ # Replace non-alphanumeric characters with dashes
115
+ sanitized = ''.join(c if c.isalnum() else '-' for c in name)
116
+ # Remove consecutive dashes
117
+ while '--' in sanitized:
118
+ sanitized = sanitized.replace('--', '-')
119
+ # Remove leading/trailing dashes
120
+ sanitized = sanitized.strip('-')
121
+ # Limit to 255 characters (CDK limit)
122
+ return sanitized[:255]
123
+
104
124
  def _resolve_environment_variables(self) -> Dict[str, str]:
105
125
  """
106
126
  Resolve environment variables, including SSM parameter references.
107
127
  Supports {{ssm:parameter-path}} syntax for dynamic SSM lookups.
108
128
  Uses CDK tokens that resolve at deployment time, not synthesis time.
129
+ Caches results to prevent duplicate construct creation.
109
130
  """
131
+ # Return cached result if available
132
+ if self._resolved_env_cache is not None:
133
+ return self._resolved_env_cache
134
+
110
135
  resolved_env = {}
111
136
 
112
137
  for key, value in self.edge_config.environment.items():
@@ -115,24 +140,29 @@ class LambdaEdgeStack(IStack, StandardizedSsmMixin):
115
140
  # Extract SSM parameter path
116
141
  ssm_param_path = value[6:-2] # Remove {{ssm: and }}
117
142
 
143
+ # Create deterministic construct name from parameter path
144
+ construct_name = self._sanitize_construct_name(f"env-{key}-{ssm_param_path}")
145
+
118
146
  # Import SSM parameter - this creates a token that resolves at deployment time
119
147
  param = ssm.StringParameter.from_string_parameter_name(
120
148
  self,
121
- f"env-{key}-{hash(ssm_param_path) % 10000}",
149
+ construct_name,
122
150
  ssm_param_path
123
151
  )
124
152
  resolved_value = param.string_value
125
- logger.info(f"Resolved environment variable {key} from SSM {ssm_param_path}")
153
+ logger.info(f"Resolved environment variable {key} from SSM {ssm_param_path} as {construct_name}")
126
154
  resolved_env[key] = resolved_value
127
155
  else:
128
156
  resolved_env[key] = value
129
157
 
158
+ # Cache the result
159
+ self._resolved_env_cache = resolved_env
130
160
  return resolved_env
131
161
 
132
162
  def _create_lambda_function(self, function_name: str) -> None:
133
163
  """Create the Lambda function"""
134
164
 
135
- # Resolve code path - support package references (e.g., "cdk_factory:lambdas/edge/ip_gate")
165
+ # Resolve code path - support package references (e.g., "cdk_factory:lambdas/cloudfront/ip_gate")
136
166
  code_path_str = self.edge_config.code_path
137
167
 
138
168
  if ':' in code_path_str:
@@ -185,10 +215,12 @@ class LambdaEdgeStack(IStack, StandardizedSsmMixin):
185
215
  # Create runtime configuration file for Lambda@Edge
186
216
  # Since Lambda@Edge doesn't support environment variables, we bundle a config file
187
217
  # Use the full function_name (e.g., "tech-talk-dev-ip-gate") not just the base name
218
+ resolved_env = self._resolve_environment_variables()
188
219
  runtime_config = {
189
220
  'environment': self.deployment.environment,
190
221
  'function_name': function_name,
191
- 'region': self.deployment.region
222
+ 'region': self.deployment.region,
223
+ 'environment_variables': resolved_env # Add actual environment variables
192
224
  }
193
225
 
194
226
  runtime_config_path = temp_code_dir / 'runtime_config.json'
@@ -239,7 +271,8 @@ class LambdaEdgeStack(IStack, StandardizedSsmMixin):
239
271
  f"{function_name}-Role",
240
272
  assumed_by=iam.CompositePrincipal(
241
273
  iam.ServicePrincipal("lambda.amazonaws.com"),
242
- iam.ServicePrincipal("edgelambda.amazonaws.com")
274
+ iam.ServicePrincipal("edgelambda.amazonaws.com"),
275
+ iam.ServicePrincipal("cloudfront.amazonaws.com") # Add CloudFront service principal
243
276
  ),
244
277
  description=f"Execution role for Lambda@Edge function {function_name}",
245
278
  managed_policies=[
@@ -285,6 +318,36 @@ class LambdaEdgeStack(IStack, StandardizedSsmMixin):
285
318
  for key, value in self.edge_config.tags.items():
286
319
  cdk.Tags.of(self.function).add(key, value)
287
320
 
321
+ # Add resource-based policy allowing CloudFront to invoke the Lambda function
322
+ # This is REQUIRED for Lambda@Edge to work properly
323
+ permission_kwargs = {
324
+ "principal": iam.ServicePrincipal("cloudfront.amazonaws.com"),
325
+ "action": "lambda:InvokeFunction",
326
+ }
327
+
328
+ # Optional: Add source ARN restriction if CloudFront distribution ARN is available
329
+ # This provides more secure permission scoping
330
+ distribution_arn_path = f"/{self.deployment.environment}/{self.workload.name}/cloudfront/arn"
331
+ try:
332
+ distribution_arn = ssm.StringParameter.from_string_parameter_name(
333
+ self,
334
+ "cloudfront-distribution-arn",
335
+ distribution_arn_path
336
+ ).string_value
337
+
338
+ # Add source ARN condition for more secure permission scoping
339
+ permission_kwargs["source_arn"] = distribution_arn
340
+ logger.info(f"Adding CloudFront permission with source ARN restriction: {distribution_arn}")
341
+ except Exception:
342
+ # Distribution ARN not available (common during initial deployment)
343
+ # CloudFront will scope the permission appropriately when it associates the Lambda
344
+ logger.warning(f"CloudFront distribution ARN not found at {distribution_arn_path}, using open permission")
345
+
346
+ self.function.add_permission(
347
+ "CloudFrontInvokePermission",
348
+ **permission_kwargs
349
+ )
350
+
288
351
  def _create_function_version(self, function_name: str) -> None:
289
352
  """
290
353
  Create a version of the Lambda function.
@@ -300,33 +363,61 @@ class LambdaEdgeStack(IStack, StandardizedSsmMixin):
300
363
  f"Version for Lambda@Edge deployment - {self.edge_config.description}"
301
364
  )
302
365
 
366
+ def _configure_edge_log_retention(self, function_name: str) -> None:
367
+ """
368
+ Configure log retention for Lambda@Edge regional logs.
369
+
370
+ Lambda@Edge creates log groups in multiple regions that need
371
+ separate retention configuration from the primary log group.
372
+ """
373
+ from aws_cdk import custom_resources as cr
374
+
375
+ # Get edge log retention from config (default to same as primary logs)
376
+ edge_retention_days = self.edge_config.dictionary.get("edge_log_retention_days", 7)
377
+
378
+ # List of common Lambda@Edge regions
379
+ edge_regions = [
380
+ 'us-east-1', 'us-east-2', 'us-west-1', 'us-west-2',
381
+ 'eu-west-1', 'eu-west-2', 'eu-central-1',
382
+ 'ap-southeast-1', 'ap-southeast-2', 'ap-northeast-1',
383
+ 'ca-central-1', 'sa-east-1'
384
+ ]
385
+
386
+ # Create custom resource to set log retention for each region
387
+ for region in edge_regions:
388
+ log_group_name = f"/aws/lambda/{region}.{function_name}"
389
+
390
+ # Use AwsCustomResource to set log retention
391
+ cr.AwsCustomResource(
392
+ self, f"EdgeLogRetention-{region}",
393
+ on_update={
394
+ "service": "Logs",
395
+ "action": "putRetentionPolicy",
396
+ "parameters": {
397
+ "logGroupName": log_group_name,
398
+ "retentionInDays": edge_retention_days
399
+ },
400
+ "physical_resource_id": cr.PhysicalResourceId.from_response("logGroupName")
401
+ },
402
+ on_delete={
403
+ "service": "Logs",
404
+ "action": "deleteRetentionPolicy",
405
+ "parameters": {
406
+ "logGroupName": log_group_name
407
+ },
408
+ "physical_resource_id": cr.PhysicalResourceId.from_response("logGroupName")
409
+ },
410
+ policy=cr.AwsCustomResourcePolicy.from_sdk_calls(
411
+ resources=[f"arn:aws:logs:{region}:*:log-group:{log_group_name}*"]
412
+ )
413
+ )
414
+
415
+ logger.info(f"Configured edge log retention to {edge_retention_days} days for {len(edge_regions)} regions")
416
+
303
417
  def _add_outputs(self, function_name: str) -> None:
304
418
  """Add CloudFormation outputs and SSM exports"""
305
419
 
306
- # CloudFormation outputs
307
- cdk.CfnOutput(
308
- self,
309
- "FunctionName",
310
- value=self.function.function_name,
311
- description="Lambda function name",
312
- export_name=f"{function_name}-name"
313
- )
314
-
315
- cdk.CfnOutput(
316
- self,
317
- "FunctionArn",
318
- value=self.function.function_arn,
319
- description="Lambda function ARN (unversioned)",
320
- export_name=f"{function_name}-arn"
321
- )
322
420
 
323
- cdk.CfnOutput(
324
- self,
325
- "FunctionVersionArn",
326
- value=self.function_version.function_arn,
327
- description="Lambda function version ARN (use this for Lambda@Edge)",
328
- export_name=f"{function_name}-version-arn"
329
- )
330
421
 
331
422
  # SSM Parameter Store exports (if configured)
332
423
  ssm_exports = self.edge_config.dictionary.get("ssm", {}).get("exports", {})
@@ -656,36 +656,7 @@ class LoadBalancerStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
656
656
 
657
657
  def _export_cfn_outputs(self, lb_name: str) -> None:
658
658
  """Add CloudFormation outputs for the Load Balancer"""
659
- if self.load_balancer:
660
- # Load Balancer DNS Name
661
- cdk.CfnOutput(
662
- self,
663
- f"{lb_name}-dns-name",
664
- value=self.load_balancer.load_balancer_dns_name,
665
- export_name=f"{self.deployment.build_resource_name(lb_name)}-dns-name",
666
- )
667
-
668
- # Load Balancer ARN
669
- cdk.CfnOutput(
670
- self,
671
- f"{lb_name}-arn",
672
- value=self.load_balancer.load_balancer_arn,
673
- export_name=f"{self.deployment.build_resource_name(lb_name)}-arn",
674
- )
675
-
676
- # Target Group ARNs
677
- for tg_name, target_group in self.target_groups.items():
678
- # Normalize target group name for consistent CloudFormation export naming
679
- normalized_tg_name = self.normalize_resource_name(
680
- tg_name, for_export=True
681
- )
682
- cdk.CfnOutput(
683
- self,
684
- f"{lb_name}-{normalized_tg_name}-arn",
685
- value=target_group.target_group_arn,
686
- export_name=f"{self.deployment.build_resource_name(lb_name)}-{normalized_tg_name}-arn",
687
- )
688
-
659
+ return
689
660
  def _export_ssm_parameters(self, lb_name: str) -> None:
690
661
  """Export Load Balancer resources to SSM Parameter Store if configured"""
691
662
  if not self.load_balancer:
@@ -716,32 +687,4 @@ class LoadBalancerStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
716
687
 
717
688
  def _export_cfn_outputs(self, lb_name: str) -> None:
718
689
  """Add CloudFormation outputs for the Load Balancer"""
719
- if self.load_balancer:
720
- # Load Balancer DNS Name
721
- cdk.CfnOutput(
722
- self,
723
- f"{lb_name}-dns-name",
724
- value=self.load_balancer.load_balancer_dns_name,
725
- export_name=f"{self.deployment.build_resource_name(lb_name)}-dns-name",
726
- )
727
-
728
- # Load Balancer ARN
729
- cdk.CfnOutput(
730
- self,
731
- f"{lb_name}-arn",
732
- value=self.load_balancer.load_balancer_arn,
733
- export_name=f"{self.deployment.build_resource_name(lb_name)}-arn",
734
- )
735
-
736
- # Target Group ARNs
737
- for tg_name, target_group in self.target_groups.items():
738
- # Normalize target group name for consistent CloudFormation export naming
739
- normalized_tg_name = self.normalize_resource_name(
740
- tg_name, for_export=True
741
- )
742
- cdk.CfnOutput(
743
- self,
744
- f"{lb_name}-{normalized_tg_name}-arn",
745
- value=target_group.target_group_arn,
746
- export_name=f"{self.deployment.build_resource_name(lb_name)}-{normalized_tg_name}-arn",
747
- )
690
+ return
@@ -285,31 +285,7 @@ class RdsStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
285
285
 
286
286
  def _add_outputs(self, db_name: str) -> None:
287
287
  """Add CloudFormation outputs for the RDS instance"""
288
- if self.db_instance:
289
- # Database endpoint
290
- cdk.CfnOutput(
291
- self,
292
- f"{db_name}-endpoint",
293
- value=self.db_instance.db_instance_endpoint_address,
294
- export_name=f"{self.deployment.build_resource_name(db_name)}-endpoint",
295
- )
296
-
297
- # Database port
298
- cdk.CfnOutput(
299
- self,
300
- f"{db_name}-port",
301
- value=self.db_instance.db_instance_endpoint_port,
302
- export_name=f"{self.deployment.build_resource_name(db_name)}-port",
303
- )
304
-
305
- # Secret ARN (if available)
306
- if hasattr(self.db_instance, "secret") and self.db_instance.secret:
307
- cdk.CfnOutput(
308
- self,
309
- f"{db_name}-secret-arn",
310
- value=self.db_instance.secret.secret_arn,
311
- export_name=f"{self.deployment.build_resource_name(db_name)}-secret-arn",
312
- )
288
+ return
313
289
 
314
290
  def _export_ssm_parameters(self, db_name: str) -> None:
315
291
  """Export RDS connection info and credentials to SSM Parameter Store"""
@@ -413,37 +413,4 @@ class Route53Stack(IStack, StandardizedSsmMixin):
413
413
  def _add_outputs(self) -> None:
414
414
  """Add CloudFormation outputs for the Route53 resources"""
415
415
  # Hosted Zone ID
416
- if self.hosted_zone:
417
- cdk.CfnOutput(
418
- self,
419
- "HostedZoneId",
420
- value=self.hosted_zone.hosted_zone_id,
421
- export_name=f"{self.deployment.build_resource_name('hosted-zone')}-id"
422
- )
423
-
424
- # Hosted Zone Name Servers
425
- if hasattr(self.hosted_zone, "name_servers") and self.hosted_zone.name_servers:
426
- cdk.CfnOutput(
427
- self,
428
- "NameServers",
429
- value=",".join(self.hosted_zone.name_servers),
430
- export_name=f"{self.deployment.build_resource_name('hosted-zone')}-name-servers"
431
- )
432
-
433
- # Certificate ARN
434
- if self.certificate:
435
- cdk.CfnOutput(
436
- self,
437
- "CertificateArn",
438
- value=self.certificate.certificate_arn,
439
- export_name=f"{self.deployment.build_resource_name('certificate')}-arn"
440
- )
441
-
442
- # Record names
443
- for name, record in self.records.items():
444
- cdk.CfnOutput(
445
- self,
446
- f"Record-{name}",
447
- value=name,
448
- export_name=f"{self.deployment.build_resource_name('record')}-{name}"
449
- )
416
+ return
@@ -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
- if self.security_group:
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
- for queue_name, queue in self.queues.items():
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
@@ -241,115 +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
- if not self.vpc:
245
- return
246
-
247
- # VPC outputs
248
- # Use workload environment for consistency
249
- workload_env = self.workload.dictionary.get("environment", self.deployment.environment)
250
- workload_name = self.workload.dictionary.get("name", self.deployment.workload_name)
251
-
252
- cdk.CfnOutput(
253
- self,
254
- f"{vpc_name}-VpcId",
255
- value=self.vpc.vpc_id,
256
- description=f"VPC ID for {vpc_name}",
257
- export_name=f"{workload_name}-{workload_env}-vpc-id",
258
- )
259
-
260
- # Subnet outputs
261
- public_subnet_ids = [subnet.subnet_id for subnet in self.vpc.public_subnets]
262
- if public_subnet_ids:
263
- cdk.CfnOutput(
264
- self,
265
- f"{vpc_name}-PublicSubnetIds",
266
- value=",".join(public_subnet_ids),
267
- description=f"Public subnet IDs for {vpc_name}",
268
- export_name=f"{workload_name}-{workload_env}-public-subnet-ids",
269
- )
270
-
271
- private_subnet_ids = [subnet.subnet_id for subnet in self.vpc.private_subnets]
272
- if private_subnet_ids:
273
- cdk.CfnOutput(
274
- self,
275
- f"{vpc_name}-PrivateSubnetIds",
276
- value=",".join(private_subnet_ids),
277
- description=f"Private subnet IDs for {vpc_name}",
278
- export_name=f"{workload_name}-{workload_env}-private-subnet-ids",
279
- )
280
-
281
- isolated_subnet_ids = [subnet.subnet_id for subnet in self.vpc.isolated_subnets]
282
- if isolated_subnet_ids:
283
- cdk.CfnOutput(
284
- self,
285
- f"{vpc_name}-IsolatedSubnetIds",
286
- value=",".join(isolated_subnet_ids),
287
- description=f"Isolated subnet IDs for {vpc_name}",
288
- export_name=f"{workload_name}-{workload_env}-isolated-subnet-ids",
289
- )
290
-
291
- # Route table outputs - simplified to avoid route table access issues
292
- # Skip route table outputs for now as they're causing CDK API issues
293
- # public_route_table_ids = []
294
- # if self.vpc.public_subnets:
295
- # for subnet in self.vpc.public_subnets:
296
- # # Access route table through the subnet's route table association
297
- # for association in subnet.node.children:
298
- # if hasattr(association, 'route_table_id') and association.route_table_id:
299
- # public_route_table_ids.append(association.route_table_id)
300
- #
301
- # if public_route_table_ids:
302
- # cdk.CfnOutput(
303
- # self,
304
- # f"{vpc_name}-PublicRouteTableIds",
305
- # value=",".join(public_route_table_ids),
306
- # description=f"Public route table IDs for {vpc_name}",
307
- # export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-public-route-table-ids",
308
- # )
309
- #
310
- # private_route_table_ids = []
311
- # if self.vpc.private_subnets:
312
- # for subnet in self.vpc.private_subnets:
313
- # # Access route table through the subnet's route table association
314
- # for association in subnet.node.children:
315
- # if hasattr(association, 'route_table_id') and association.route_table_id:
316
- # private_route_table_ids.append(association.route_table_id)
317
- #
318
- # if private_route_table_ids:
319
- # cdk.CfnOutput(
320
- # self,
321
- # f"{vpc_name}-PrivateRouteTableIds",
322
- # value=",".join(private_route_table_ids),
323
- # description=f"Private route table IDs for {vpc_name}",
324
- # export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-private-route-table-ids",
325
- # )
326
-
327
- # Internet Gateway output
328
- if hasattr(self.vpc, 'internet_gateway_id') and self.vpc.internet_gateway_id:
329
- cdk.CfnOutput(
330
- self,
331
- f"{vpc_name}-InternetGatewayId",
332
- value=self.vpc.internet_gateway_id,
333
- description=f"Internet Gateway ID for {vpc_name}",
334
- export_name=f"{workload_name}-{workload_env}-internet-gateway-id",
335
- )
336
-
337
- # NAT Gateway outputs - simplified to avoid None values
338
- nat_gateway_ids = []
339
- for subnet in self.vpc.public_subnets:
340
- if hasattr(subnet, 'node') and subnet.node:
341
- for child in subnet.node.children:
342
- if hasattr(child, 'nat_gateway_id') and child.nat_gateway_id:
343
- nat_gateway_ids.append(child.nat_gateway_id)
344
-
345
- if nat_gateway_ids:
346
- cdk.CfnOutput(
347
- self,
348
- f"{vpc_name}-NatGatewayIds",
349
- value=",".join(nat_gateway_ids),
350
- description=f"NAT Gateway IDs for {vpc_name}",
351
- export_name=f"{workload_name}-{workload_env}-nat-gateway-ids",
352
- )
244
+ return
245
+
353
246
 
354
247
  def _export_ssm_parameters(self) -> None:
355
248
  """Export SSM parameters using standardized approach"""
@@ -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
- candidate = Path(os.path.join(Path(base), source)).resolve()
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,
cdk_factory/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.18.21"
1
+ __version__ = "0.19.13"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cdk_factory
3
- Version: 0.18.21
3
+ Version: 0.19.13
4
4
  Summary: CDK Factory. A QuickStarter and best practices setup for CDK projects
5
5
  Author-email: Eric Wilson <eric.wilson@geekcafe.com>
6
6
  License: MIT License
@@ -2,7 +2,7 @@ cdk_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cdk_factory/app.py,sha256=RnX0-pwdTAPAdKJK_j13Zl8anf9zYKBwboR0KA8K8xM,10346
3
3
  cdk_factory/cdk.json,sha256=SKZKhJ2PBpFH78j-F8S3VDYW-lf76--Q2I3ON-ZIQfw,3106
4
4
  cdk_factory/cli.py,sha256=FGbCTS5dYCNsfp-etshzvFlGDCjC28r6rtzYbe7KoHI,6407
5
- cdk_factory/version.py,sha256=B2Sy7twJt6907f9y1FOcoO6wzQSV9i7I4wVmuy06Voc,24
5
+ cdk_factory/version.py,sha256=qfLAZUWz8ZIxPUsFWlqQCfCwTNmUwrL2jNL4w6ZRFi0,24
6
6
  cdk_factory/builds/README.md,sha256=9BBWd7bXpyKdMU_g2UljhQwrC9i5O_Tvkb6oPvndoZk,90
7
7
  cdk_factory/commands/command_loader.py,sha256=QbLquuP_AdxtlxlDy-2IWCQ6D-7qa58aphnDPtp_uTs,3744
8
8
  cdk_factory/configurations/base_config.py,sha256=eJ3Pl3GWk1jVr_bYQaaWlw4_-ZiFGaiXllI_fOOX1i0,9323
@@ -34,25 +34,25 @@ cdk_factory/configurations/resources/ecr.py,sha256=iJEtKqBT7vQU0LU4urIglraIR7cPZ
34
34
  cdk_factory/configurations/resources/ecs_cluster.py,sha256=mQYJu7SUPDl5E4dMR6HCPFoWvFA3RGIb0iMNn-K7LX8,3635
35
35
  cdk_factory/configurations/resources/ecs_service.py,sha256=bOWjVECd6Kbc5NGGSnDaopnKrjRsUfmaZ6-qrsmTs3Q,6468
36
36
  cdk_factory/configurations/resources/exisiting.py,sha256=EVOLnkB-DGfTlmDgyQ5DD5k2zYfpFxqI3gugDR7mifI,478
37
- cdk_factory/configurations/resources/lambda_edge.py,sha256=MjmiwDkys4aoRvDQhH3MT6BgeShzJXNWL7761HJrLtQ,3404
37
+ cdk_factory/configurations/resources/lambda_edge.py,sha256=C0S6HrQe2QPfmhj1PhZ7FEnT2EidOg5t-pUFUsOzaDc,3855
38
38
  cdk_factory/configurations/resources/lambda_function.py,sha256=VENZ9-ABJ5mjcN8J8wdLH4KHDYr1kWO0iFDH0B2mJXA,14659
39
39
  cdk_factory/configurations/resources/lambda_layers.py,sha256=gVeP_-LC3Eq0lkPaG_JfFUwboM5evRPr99SfKj53m7A,633
40
40
  cdk_factory/configurations/resources/lambda_triggers.py,sha256=MD7cdMNKEulNBhtMLIFnWJuJ5R-yyIqa0LHUgbSQerA,834
41
41
  cdk_factory/configurations/resources/load_balancer.py,sha256=P-jKemIjIWWqScmQKspmRy1m3BrwNkRtTNHDStOAJds,5617
42
42
  cdk_factory/configurations/resources/monitoring.py,sha256=CPYWbUbWQzoPqDhdPiB4Vahq-pPi6BEkavkVohadSIo,2422
43
- cdk_factory/configurations/resources/rds.py,sha256=ap2sbrcPsesfaPSluzyomGgT70_PUg9B39smVw6fvEM,15927
43
+ cdk_factory/configurations/resources/rds.py,sha256=pX6uihguv1apYH93obcE4RloQDP48q5JHnYTnO39Pn0,15935
44
44
  cdk_factory/configurations/resources/resource_mapping.py,sha256=cwv3n63RJ6E59ErsmSTdkW4i-g8huhHtKI0ExbRhJxA,2182
45
45
  cdk_factory/configurations/resources/resource_naming.py,sha256=VE9S2cpzp11qqPL2z1sX79wXH0o1SntO2OG74nEmWC8,5508
46
46
  cdk_factory/configurations/resources/resource_types.py,sha256=1WQHyDoErb-M-tETZZzyLDtbq_jdC85-I403dM48pgE,2317
47
47
  cdk_factory/configurations/resources/route53.py,sha256=u63kw9cLBdOQrvxnULmopFqiArIYNvoWhrILNWvhD7w,3599
48
48
  cdk_factory/configurations/resources/route53_hosted_zone.py,sha256=qjEYPCSxSOx5blr9EULv892ezxkCs--yrLa1ngWbyXM,880
49
49
  cdk_factory/configurations/resources/rum.py,sha256=KgC2Mxhtr5XrICVXdgOXmxYp0GKu9lBs7izfG-Re9Ck,5294
50
- cdk_factory/configurations/resources/s3.py,sha256=kLY0XNCfErNYnOwekefFJxEJ_AKPifA-wSaaNczDp94,6036
50
+ cdk_factory/configurations/resources/s3.py,sha256=3RVGvHLEnxa-hCgDXsqZ9nq8Aic0zW0JVgMdybl95is,6407
51
51
  cdk_factory/configurations/resources/security_group.py,sha256=8kQtaaRVEn2aDm8XoC7QFh2mDOFbPbgobmssIuqU8MA,2259
52
52
  cdk_factory/configurations/resources/security_group_full_stack.py,sha256=CujSl6mfPlVO0Dxso8fDjwW5VZB0vbeP9HqbcTaM8H4,2657
53
53
  cdk_factory/configurations/resources/sqs.py,sha256=fAh2dqttJ6PX46enFRULuiLEu3TEj0Vb2xntAOgUpYE,4346
54
54
  cdk_factory/configurations/resources/vpc.py,sha256=_W77du9wf8SJM64JbEAxEOQYerOm6aw4YaSsE7A_kKE,4453
55
- cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py,sha256=LHgjvTNghCMTpHh90VWl7AbE100Er-S9EgyEVt12J_c,25809
55
+ cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py,sha256=WbbI37Ez262kffJNNpvyjXtFwOQlNfRql0NGBCLjpgE,25775
56
56
  cdk_factory/constructs/ecr/ecr_construct.py,sha256=jEimLwLvO5ERuFEn_L2q2pyPKgkdHQ6Oi7Sy990ttSg,11002
57
57
  cdk_factory/constructs/lambdas/lambda_function_construct.py,sha256=SQ5SEXn4kezVAzXuv_A_JB3o_svyBXOMi-htvfB9HQs,4516
58
58
  cdk_factory/constructs/lambdas/lambda_function_docker_construct.py,sha256=O8aiHpNQ59eE3qEttEHVxbvp06v4byXOeYCVTAOI_Cg,9993
@@ -83,42 +83,42 @@ cdk_factory/stack/stack_modules.py,sha256=kgEK-j0smZPozVwTCfM1g1V17EyTBT0TXAQZq4
83
83
  cdk_factory/stack_library/__init__.py,sha256=_v4kz9EYAjox6strrTK_4fb9GloJ2Kyhf63VRPivl2U,638
84
84
  cdk_factory/stack_library/stack_base.py,sha256=Cu3qeqPQf33QaaXoxk_EaziNCIXcyspOo5AU3eX_wyM,5140
85
85
  cdk_factory/stack_library/acm/__init__.py,sha256=4FNRLykblcKZvq_wieYwvv9N_jgrZnJ7ECH9xKh-0Ls,81
86
- cdk_factory/stack_library/acm/acm_stack.py,sha256=QJ3GkT17PmWoGkfO5Um02hvrfyJ9HbiPMnclwDP7IbA,5846
86
+ cdk_factory/stack_library/acm/acm_stack.py,sha256=LW4QgzcMDvtSpqwfc4ykgpzDGvXe4udvWVE_DtBN4Zg,5414
87
87
  cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=PvLdGvcopGpLP0FwpfUcfXNiTIfYLTXqrG-TniE38yc,39643
88
88
  cdk_factory/stack_library/auto_scaling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
- cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py,sha256=KfuNTKpDCQmvbw4F71sZBTkDFiBn1jkxxIojl21ExvQ,24140
89
+ cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py,sha256=G8OOgTAfsTDVArvuDfzuNpuGHzqz8kREHnnkMtjulAQ,25334
90
90
  cdk_factory/stack_library/aws_lambdas/lambda_stack.py,sha256=SFbBPvvCopbyiuYtq-O5sQkFCf94Wzua6aDUXiFDSB4,26161
91
91
  cdk_factory/stack_library/buckets/README.md,sha256=XkK3UNVtRLE7NtUvbhCOBBYUYi8hlrrSaI1s3GJVrqI,78
92
92
  cdk_factory/stack_library/buckets/bucket_stack.py,sha256=SLoZqSffAqmeBBEVUQg54D_8Ad5UKdkjEAmKAVgAqQo,1778
93
93
  cdk_factory/stack_library/cloudfront/__init__.py,sha256=Zfx50q4xIJ4ZEoVIzUBDTKbRE9DKDM6iyVIFhtQXvww,153
94
- cdk_factory/stack_library/cloudfront/cloudfront_stack.py,sha256=yuNs_W5b85JSXXv6_17OAD-e-wUitDPrRlZV4bxjChU,31459
95
- cdk_factory/stack_library/code_artifact/code_artifact_stack.py,sha256=k831b_fAFoXSiwj5cencnCQzUSeuKIUyVCp6Ev_TMgI,6274
94
+ cdk_factory/stack_library/cloudfront/cloudfront_stack.py,sha256=7cYPqoQyiXH6r3j9jp9oLXv1ZDixeCYPAXJtTOmagPc,32309
95
+ cdk_factory/stack_library/code_artifact/code_artifact_stack.py,sha256=o86cmC_ZV82z-K7DoAR0u1nAieoTi-vxRF01tyJn-9M,5297
96
96
  cdk_factory/stack_library/cognito/cognito_stack.py,sha256=3tjKCNcIwXZn7fd4EDQdY6H9m6CnZohI4uTQ4TpacRQ,25327
97
97
  cdk_factory/stack_library/dynamodb/dynamodb_stack.py,sha256=-_Ij1zXIxUuZIWgdevam_1vD3LEJ6pFs9U0hmw0KwIw,6743
98
98
  cdk_factory/stack_library/ecr/README.md,sha256=xw2wPx9WN03Y4BBwqvbi9lAFGNyaD1FUNpqxVJX14Oo,179
99
99
  cdk_factory/stack_library/ecr/ecr_stack.py,sha256=KLbd5WN5-ZiojsS5wJ4PX-tIL0cCylCSvXjO6sVrgWY,2102
100
100
  cdk_factory/stack_library/ecs/__init__.py,sha256=o5vGDtD_h-gVXb3-Ysr8xUNpEcMsnmMVgZv2Pupcdow,219
101
101
  cdk_factory/stack_library/ecs/ecs_cluster_stack.py,sha256=sAPTLU5CAwMoLTW_pNy_cd0OtVkfDR7IxxsSq5AE0yo,12091
102
- cdk_factory/stack_library/ecs/ecs_service_stack.py,sha256=EylXlAHTk0hx9IAl3VfW2hauE-J2jUnAFjsNMe73-ZM,27725
102
+ cdk_factory/stack_library/ecs/ecs_service_stack.py,sha256=KB4YCIsMm5JIGM9Bm-bKcr3eX5xXFgnoA7jST_ekK44,28209
103
103
  cdk_factory/stack_library/lambda_edge/__init__.py,sha256=ByBJ_CWdc4UtTmFBZH-6pzBMNkjkdtE65AmnB0Fs6lM,156
104
- cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py,sha256=C_h2Xb2_zaCGiw5otbeSozOLNbxaciMLo8FX_TQOAlw,16409
104
+ cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py,sha256=eHh_k4mbNp1prEnNvKqfK82lLNrMZS-7HAaUYzFEoOU,21040
105
105
  cdk_factory/stack_library/load_balancer/__init__.py,sha256=wZpKw2OecLJGdF5mPayCYAEhu2H3c2gJFFIxwXftGDU,52
106
- cdk_factory/stack_library/load_balancer/load_balancer_stack.py,sha256=iiSxjuQOflF6_GLBS1xoK14fK2r47tpaI03EMvmE8V8,30693
106
+ cdk_factory/stack_library/load_balancer/load_balancer_stack.py,sha256=ApW5q3SAvSJtiK0RInNljmubqXqKZU5QBAaUoeIW-pM,28287
107
107
  cdk_factory/stack_library/monitoring/__init__.py,sha256=k1G_KDx47Aw0UugaL99PN_TKlyLK4nkJVApCaAK7GJg,153
108
108
  cdk_factory/stack_library/monitoring/monitoring_stack.py,sha256=N_1YvEXE7fboH_S3kv_dSKZsufxMuPdFMjGzlNFpuSo,19283
109
109
  cdk_factory/stack_library/rds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
110
- cdk_factory/stack_library/rds/rds_stack.py,sha256=5dk_SUMkbnSalD2AKI0c-LrKbaHB4OOgq6DmRa4QMbM,15377
110
+ cdk_factory/stack_library/rds/rds_stack.py,sha256=VToW-uFKAfilyhN4T8-TXaFW8f_VuXEIuUoBHvDN0Ns,14398
111
111
  cdk_factory/stack_library/route53/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
112
- cdk_factory/stack_library/route53/route53_stack.py,sha256=4OeJxBTchcXHZznUQ6IIMuFmbsWtOoR0cIfDIIdHs8o,19624
112
+ cdk_factory/stack_library/route53/route53_stack.py,sha256=taREOw-s7i1NZcdR-za1lKkj-Cj8UPmr4BO5txS18fI,18348
113
113
  cdk_factory/stack_library/rum/__init__.py,sha256=gUrWQdzd4rZ2J0YzAQC8PsEGAS7QgyYjB2ZCUKWasy4,90
114
114
  cdk_factory/stack_library/rum/rum_stack.py,sha256=c67m0Jbyx8hx9TTx9TBBhZMDqtSK7QCqKx_Ec1t8LgY,14067
115
115
  cdk_factory/stack_library/security_group/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
116
- cdk_factory/stack_library/security_group/security_group_full_stack.py,sha256=yvZ5QS9eDj_LkkwHY_Pcb37lOVPbGMfSdizZa1X6BEI,13561
117
- cdk_factory/stack_library/security_group/security_group_stack.py,sha256=Zv9FCEHvSBT1cM9bXOtyIUFwhRHKCSTgvaqOyhGj0wg,14456
118
- cdk_factory/stack_library/simple_queue_service/sqs_stack.py,sha256=jJksWrvrvgZUMM01RZ317DOIxqIJbkYYSYu38w0jHpc,6039
116
+ cdk_factory/stack_library/security_group/security_group_full_stack.py,sha256=Y9JYD0QpKNe11fuCPjWlV1rucX1DMm7bs8Eu4t4XDqw,12204
117
+ cdk_factory/stack_library/security_group/security_group_stack.py,sha256=uthk_MX2gNfA6a7gR7PjEfjov4WgTTtJ09G0cJOFWko,14168
118
+ cdk_factory/stack_library/simple_queue_service/sqs_stack.py,sha256=HSJgWo4Wl6nQ5rJFQXOkDTyd_6PuHhx5VyaZLVvmYM8,4891
119
119
  cdk_factory/stack_library/vpc/__init__.py,sha256=7pIqP97Gf2AJbv9Ebp1WbQGHYhgEbWJ52L1MzeXBybA,42
120
- cdk_factory/stack_library/vpc/vpc_stack.py,sha256=AUq5oAJa1OrtUIqPZlnco0jSBOyOQvV3tEspIe2ZAGc,19451
121
- cdk_factory/stack_library/websites/static_website_stack.py,sha256=jnB-u6x37w2tsheG7WrKBY50WIx1AnCKoVh7mbT4rZU,11254
120
+ cdk_factory/stack_library/vpc/vpc_stack.py,sha256=8uFyPIhAu-4A3iclTFlOiJrEZuYu67gI5UtiDSzmFF4,14461
121
+ cdk_factory/stack_library/websites/static_website_stack.py,sha256=twpR9y4bxDx2GcEn0uP8PFNbsrgb8G9juZfeuHj7VqE,11433
122
122
  cdk_factory/stages/websites/static_website_stage.py,sha256=X4fpKXkhb0zIbSHx3QyddBhVSLBryb1vf1Cg2fMTqog,755
123
123
  cdk_factory/templates/README.md,sha256=ATBEjG6beYvbEAdLtZ_8xnxgFD5X0cgZoI_6pToqH90,2679
124
124
  cdk_factory/templates/app.py.template,sha256=aM60x0nNV80idtCL8jm1EddY63F5tDITYOlavg-BPMU,1069
@@ -136,8 +136,8 @@ cdk_factory/utilities/os_execute.py,sha256=5Op0LY_8Y-pUm04y1k8MTpNrmQvcLmQHPQITE
136
136
  cdk_factory/utils/api_gateway_utilities.py,sha256=If7Xu5s_UxmuV-kL3JkXxPLBdSVUKoLtohm0IUFoiV8,4378
137
137
  cdk_factory/validation/config_validator.py,sha256=Pb0TkLiPFzUplBOgMorhRCVm08vEzZhRU5xXCDTa5CA,17602
138
138
  cdk_factory/workload/workload_factory.py,sha256=yDI3cRhVI5ELNDcJPLpk9UY54Uind1xQoV3spzT4z7E,6068
139
- cdk_factory-0.18.21.dist-info/METADATA,sha256=cf8ZcJ_UApVTnZw3r8uwjqkYzfgTUxEkURAwg1KNX7g,2452
140
- cdk_factory-0.18.21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
141
- cdk_factory-0.18.21.dist-info/entry_points.txt,sha256=S1DPe0ORcdiwEALMN_WIo3UQrW_g4YdQCLEsc_b0Swg,53
142
- cdk_factory-0.18.21.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
143
- cdk_factory-0.18.21.dist-info/RECORD,,
139
+ cdk_factory-0.19.13.dist-info/METADATA,sha256=Rw6GGM7Hl5-md3ntKrGRfX2u1ppfZRfIextBHMW-FsE,2452
140
+ cdk_factory-0.19.13.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
141
+ cdk_factory-0.19.13.dist-info/entry_points.txt,sha256=S1DPe0ORcdiwEALMN_WIo3UQrW_g4YdQCLEsc_b0Swg,53
142
+ cdk_factory-0.19.13.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
143
+ cdk_factory-0.19.13.dist-info/RECORD,,