cdk-factory 0.16.16__py3-none-any.whl → 0.17.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.
Files changed (57) hide show
  1. cdk_factory/configurations/base_config.py +23 -24
  2. cdk_factory/configurations/cdk_config.py +1 -1
  3. cdk_factory/configurations/devops.py +1 -1
  4. cdk_factory/configurations/resources/cloudfront.py +7 -2
  5. cdk_factory/configurations/resources/ecr.py +1 -1
  6. cdk_factory/configurations/resources/ecs_cluster.py +7 -5
  7. cdk_factory/configurations/resources/ecs_service.py +7 -2
  8. cdk_factory/configurations/resources/load_balancer.py +8 -9
  9. cdk_factory/configurations/resources/monitoring.py +8 -3
  10. cdk_factory/configurations/resources/rds.py +7 -8
  11. cdk_factory/configurations/resources/rum.py +7 -2
  12. cdk_factory/configurations/resources/s3.py +1 -1
  13. cdk_factory/configurations/resources/security_group_full_stack.py +7 -8
  14. cdk_factory/configurations/resources/vpc.py +19 -0
  15. cdk_factory/configurations/workload.py +32 -2
  16. cdk_factory/constructs/ecr/ecr_construct.py +9 -2
  17. cdk_factory/constructs/lambdas/policies/policy_docs.py +4 -4
  18. cdk_factory/interfaces/istack.py +4 -4
  19. cdk_factory/interfaces/networked_stack_mixin.py +6 -6
  20. cdk_factory/interfaces/standardized_ssm_mixin.py +612 -0
  21. cdk_factory/interfaces/vpc_provider_mixin.py +53 -29
  22. cdk_factory/lambdas/edge/ip_gate/handler.py +42 -40
  23. cdk_factory/pipeline/pipeline_factory.py +3 -3
  24. cdk_factory/stack_library/__init__.py +3 -2
  25. cdk_factory/stack_library/acm/acm_stack.py +2 -2
  26. cdk_factory/stack_library/api_gateway/api_gateway_stack.py +84 -59
  27. cdk_factory/stack_library/auto_scaling/auto_scaling_stack_standardized.py +530 -0
  28. cdk_factory/stack_library/code_artifact/code_artifact_stack.py +2 -2
  29. cdk_factory/stack_library/cognito/cognito_stack.py +152 -92
  30. cdk_factory/stack_library/dynamodb/dynamodb_stack.py +19 -15
  31. cdk_factory/stack_library/ecr/ecr_stack.py +2 -2
  32. cdk_factory/stack_library/ecs/__init__.py +1 -1
  33. cdk_factory/stack_library/ecs/ecs_cluster_stack_standardized.py +305 -0
  34. cdk_factory/stack_library/ecs/ecs_service_stack.py +10 -26
  35. cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +2 -2
  36. cdk_factory/stack_library/load_balancer/load_balancer_stack.py +11 -35
  37. cdk_factory/stack_library/rds/rds_stack.py +10 -27
  38. cdk_factory/stack_library/route53/route53_stack.py +2 -2
  39. cdk_factory/stack_library/rum/rum_stack.py +102 -91
  40. cdk_factory/stack_library/security_group/security_group_full_stack.py +9 -22
  41. cdk_factory/stack_library/security_group/security_group_stack.py +11 -11
  42. cdk_factory/stack_library/vpc/vpc_stack_standardized.py +411 -0
  43. cdk_factory/utilities/api_gateway_integration_utility.py +24 -16
  44. cdk_factory/utilities/environment_services.py +3 -3
  45. cdk_factory/utilities/json_loading_utility.py +1 -1
  46. cdk_factory/validation/config_validator.py +483 -0
  47. cdk_factory/version.py +1 -1
  48. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/METADATA +1 -1
  49. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/RECORD +52 -52
  50. cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -321
  51. cdk_factory/interfaces/ssm_parameter_mixin.py +0 -454
  52. cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +0 -748
  53. cdk_factory/stack_library/ecs/ecs_cluster_stack.py +0 -232
  54. cdk_factory/stack_library/vpc/vpc_stack.py +0 -298
  55. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/WHEEL +0 -0
  56. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/entry_points.txt +0 -0
  57. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,305 @@
1
+ """
2
+ ECS Cluster Stack Module (Standardized SSM Version)
3
+
4
+ Provides a dedicated stack for creating and configuring ECS clusters
5
+ with proper configurability, explicit resource management, and standardized SSM integration.
6
+ """
7
+
8
+ import logging
9
+ from typing import Optional, Dict, Any
10
+
11
+ from aws_cdk import (
12
+ aws_ecs as ecs,
13
+ aws_ec2 as ec2,
14
+ aws_iam as iam,
15
+ CfnOutput,
16
+ )
17
+ from constructs import Construct
18
+
19
+ from cdk_factory.configurations.stack import StackConfig
20
+ from cdk_factory.configurations.deployment import DeploymentConfig
21
+ from cdk_factory.configurations.workload import WorkloadConfig
22
+ from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
23
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
24
+ from cdk_factory.configurations.resources.ecs_cluster import EcsClusterConfig
25
+ from cdk_factory.stack.stack_module_registry import register_stack
26
+ from cdk_factory.interfaces.istack import IStack
27
+
28
+ logger = logging.getLogger(__name__)
29
+
30
+
31
+ @register_stack("ecs_cluster_stack")
32
+ class EcsClusterStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
33
+ """
34
+ A dedicated stack for creating and managing ECS clusters with standardized SSM integration.
35
+
36
+ This stack provides explicit configuration of ECS clusters including:
37
+ - Cluster naming
38
+ - Container insights
39
+ - Cluster settings
40
+ - Standardized SSM parameter exports
41
+ - IAM role configurations
42
+ - Template variable resolution
43
+ - Comprehensive validation
44
+ """
45
+
46
+ def __init__(self, scope: Construct, id: str, **kwargs) -> None:
47
+ """
48
+ Initialize the ECS Cluster stack.
49
+
50
+ Args:
51
+ scope: The CDK construct scope
52
+ id: The construct ID
53
+ """
54
+ super().__init__(scope, id, **kwargs)
55
+
56
+ self._initialize_vpc_cache()
57
+
58
+ self.ecs_config: Optional[EcsClusterConfig] = None
59
+ self.stack_config: Optional[StackConfig] = None
60
+ self.deployment: Optional[DeploymentConfig] = None
61
+ self.workload: Optional[WorkloadConfig] = None
62
+ self.ecs_cluster: Optional[ecs.Cluster] = None
63
+ self.instance_role: Optional[iam.Role] = None
64
+ self.instance_profile: Optional[iam.CfnInstanceProfile] = None
65
+
66
+ def build(
67
+ self,
68
+ stack_config: StackConfig,
69
+ deployment: DeploymentConfig,
70
+ workload: WorkloadConfig,
71
+ ) -> None:
72
+ """Build the ECS Cluster stack"""
73
+ self._build(stack_config, deployment, workload)
74
+
75
+ def _build(
76
+ self,
77
+ stack_config: StackConfig,
78
+ deployment: DeploymentConfig,
79
+ workload: WorkloadConfig,
80
+ ) -> None:
81
+ """Internal build method for the ECS Cluster stack"""
82
+ self.stack_config = stack_config
83
+ self.deployment = deployment
84
+ self.workload = workload
85
+
86
+ # Initialize VPC cache from mixin
87
+ self._initialize_vpc_cache()
88
+
89
+ # Load ECS cluster configuration
90
+ self.ecs_config: EcsClusterConfig = EcsClusterConfig(
91
+ stack_config.dictionary.get("ecs_cluster", {})
92
+ )
93
+
94
+ cluster_name = deployment.build_resource_name(self.ecs_config.name)
95
+
96
+ logger.info(f"Creating ECS Cluster stack: {cluster_name}")
97
+
98
+ # Setup standardized SSM integration
99
+ self.setup_standardized_ssm_integration(
100
+ scope=self,
101
+ config=self.ecs_config,
102
+ resource_type="ecs_cluster",
103
+ resource_name=cluster_name,
104
+ deployment=deployment,
105
+ workload=workload
106
+ )
107
+
108
+ # Process SSM imports using standardized method
109
+ self.process_standardized_ssm_imports()
110
+
111
+ # Create the ECS cluster
112
+ self._create_ecs_cluster()
113
+
114
+ # Create IAM roles if needed
115
+ self._create_iam_roles()
116
+
117
+ # Export cluster information
118
+ self._export_cluster_info()
119
+
120
+ # Export SSM parameters
121
+ self._export_ssm_parameters()
122
+
123
+ logger.info(f"ECS Cluster stack created: {cluster_name}")
124
+
125
+ def _create_ecs_cluster(self):
126
+ """Create the ECS cluster with explicit configuration."""
127
+ logger.info(f"Creating ECS cluster: {self.ecs_config.name}")
128
+
129
+ # Build cluster settings
130
+ cluster_settings = []
131
+
132
+ # Add container insights if enabled
133
+ if self.ecs_config.container_insights:
134
+ cluster_settings.append({"name": "containerInsights", "value": "enabled"})
135
+
136
+ # Add custom cluster settings
137
+ if self.ecs_config.cluster_settings:
138
+ cluster_settings.extend(self.ecs_config.cluster_settings)
139
+
140
+ # Get VPC using standardized approach
141
+ self.vpc = self._get_vpc()
142
+
143
+ # Create the ECS cluster
144
+ self.ecs_cluster = ecs.Cluster(
145
+ self,
146
+ "ECSCluster",
147
+ cluster_name=self.ecs_config.name,
148
+ vpc=self.vpc,
149
+ container_insights=self.ecs_config.container_insights,
150
+ default_cloud_map_namespace=(
151
+ self.ecs_config.cloud_map_namespace
152
+ if self.ecs_config.cloud_map_namespace
153
+ else None
154
+ ),
155
+ execute_command_configuration=(
156
+ self.ecs_config.execute_command_configuration
157
+ if self.ecs_config.execute_command_configuration
158
+ else None
159
+ ),
160
+ )
161
+
162
+ logger.info(f"ECS cluster created: {self.ecs_config.name}")
163
+
164
+ def _get_vpc(self):
165
+ """
166
+ Get VPC using the centralized VPC provider mixin.
167
+ """
168
+ # Use the centralized VPC resolution from VPCProviderMixin
169
+ return self.resolve_vpc(
170
+ config=self.ecs_config,
171
+ deployment=self.deployment,
172
+ workload=self.workload
173
+ )
174
+
175
+ def _create_iam_roles(self):
176
+ """Create IAM roles for the ECS cluster if configured."""
177
+ if not self.ecs_config.create_instance_role:
178
+ logger.info("Skipping instance role creation (disabled in config)")
179
+ return
180
+
181
+ logger.info("Creating ECS instance role")
182
+
183
+ # Create the instance role
184
+ self.instance_role = iam.Role(
185
+ self,
186
+ "ECSInstanceRole",
187
+ assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
188
+ managed_policies=[
189
+ iam.ManagedPolicy.from_aws_managed_policy_name("AmazonECSWorkerNodePolicy"),
190
+ iam.ManagedPolicy.from_aws_managed_policy_name("AmazonEC2ContainerRegistryReadOnly"),
191
+ iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore"),
192
+ ],
193
+ role_name=f"{self.ecs_config.name}-ecs-instance-role",
194
+ )
195
+
196
+ # Create instance profile
197
+ self.instance_profile = iam.CfnInstanceProfile(
198
+ self,
199
+ "ECSInstanceProfile",
200
+ roles=[self.instance_role.role_name],
201
+ instance_profile_name=f"{self.ecs_config.name}-ecs-instance-profile",
202
+ )
203
+
204
+ logger.info("ECS instance role and profile created")
205
+
206
+ def _export_cluster_info(self):
207
+ """Export cluster information as CloudFormation outputs."""
208
+ if not self.ecs_cluster:
209
+ return
210
+
211
+ cluster_name = self.deployment.build_resource_name(self.ecs_config.name)
212
+
213
+ # Export cluster name
214
+ CfnOutput(
215
+ self,
216
+ f"{cluster_name}-ClusterName",
217
+ value=self.ecs_cluster.cluster_name,
218
+ description=f"ECS Cluster Name for {cluster_name}",
219
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-ecs-cluster-name",
220
+ )
221
+
222
+ # Export cluster ARN
223
+ CfnOutput(
224
+ self,
225
+ f"{cluster_name}-ClusterArn",
226
+ value=self.ecs_cluster.cluster_arn,
227
+ description=f"ECS Cluster ARN for {cluster_name}",
228
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-ecs-cluster-arn",
229
+ )
230
+
231
+ # Export security group if available
232
+ if hasattr(self.ecs_cluster, 'connections') and self.ecs_cluster.connections:
233
+ security_groups = self.ecs_cluster.connections.security_groups
234
+ if security_groups:
235
+ CfnOutput(
236
+ self,
237
+ f"{cluster_name}-SecurityGroupId",
238
+ value=security_groups[0].security_group_id,
239
+ description=f"ECS Cluster Security Group ID for {cluster_name}",
240
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-ecs-cluster-sg-id",
241
+ )
242
+
243
+ # Export instance profile if created
244
+ if self.instance_profile:
245
+ CfnOutput(
246
+ self,
247
+ f"{cluster_name}-InstanceProfileArn",
248
+ value=self.instance_profile.attr_arn,
249
+ description=f"ECS Instance Profile ARN for {cluster_name}",
250
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-ecs-instance-profile-arn",
251
+ )
252
+
253
+ logger.info("ECS cluster information exported as outputs")
254
+
255
+ def _export_ssm_parameters(self) -> None:
256
+ """Export SSM parameters using standardized approach"""
257
+ if not self.ecs_cluster:
258
+ logger.warning("No ECS cluster to export")
259
+ return
260
+
261
+ # Prepare resource values for export
262
+ resource_values = {
263
+ "ecs_cluster_name": self.ecs_cluster.cluster_name,
264
+ "ecs_cluster_arn": self.ecs_cluster.cluster_arn,
265
+ }
266
+
267
+ # Add security group ID if available
268
+ if hasattr(self.ecs_cluster, 'connections') and self.ecs_cluster.connections:
269
+ security_groups = self.ecs_cluster.connections.security_groups
270
+ if security_groups:
271
+ resource_values["ecs_cluster_security_group_id"] = security_groups[0].security_group_id
272
+
273
+ # Add instance profile ARN if created
274
+ if self.instance_profile:
275
+ resource_values["ecs_instance_profile_arn"] = self.instance_profile.attr_arn
276
+
277
+ # Export using standardized SSM mixin
278
+ exported_params = self.export_standardized_ssm_parameters(resource_values)
279
+
280
+ logger.info(f"Exported SSM parameters: {exported_params}")
281
+
282
+ # Backward compatibility methods
283
+ def process_ssm_imports(self, config: Any, deployment: DeploymentConfig, resource_type: str = "resource") -> None:
284
+ """Backward compatibility method for existing modules."""
285
+ # Extract SSM configuration from old format
286
+ if hasattr(config, 'ssm_imports'):
287
+ # Convert old ssm_imports format to new format
288
+ old_imports = config.ssm_imports
289
+ new_imports = {}
290
+
291
+ for key, value in old_imports.items():
292
+ # Resolve template variables using old method
293
+ if isinstance(value, str) and not value.startswith('/'):
294
+ value = f"/{deployment.environment}/{deployment.workload_name}/{value}"
295
+ new_imports[key] = value
296
+
297
+ # Update SSM config
298
+ self.ssm_config = {"imports": new_imports}
299
+
300
+ # Process imports using standardized method
301
+ self.process_standardized_ssm_imports()
302
+
303
+
304
+ # Backward compatibility alias
305
+ EcsClusterStackStandardized = EcsClusterStack
@@ -23,7 +23,8 @@ from cdk_factory.configurations.deployment import DeploymentConfig
23
23
  from cdk_factory.configurations.stack import StackConfig
24
24
  from cdk_factory.configurations.resources.ecs_service import EcsServiceConfig
25
25
  from cdk_factory.interfaces.istack import IStack
26
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
26
+ from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
27
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
27
28
  from cdk_factory.stack.stack_module_registry import register_stack
28
29
  from cdk_factory.workload.workload_factory import WorkloadConfig
29
30
 
@@ -33,7 +34,7 @@ logger = Logger(service="EcsServiceStack")
33
34
  @register_stack("ecs_service_library_module")
34
35
  @register_stack("ecs_service_stack")
35
36
  @register_stack("fargate_service_stack")
36
- class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
37
+ class EcsServiceStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
37
38
  """
38
39
  Reusable stack for ECS/Fargate services with Docker container support.
39
40
  Supports blue-green deployments, maintenance mode, and auto-scaling.
@@ -102,30 +103,13 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
102
103
  self._add_outputs(service_name)
103
104
 
104
105
  def _load_vpc(self) -> None:
105
- """Load VPC from configuration"""
106
- # Check SSM imported values first
107
- if "vpc_id" in self.ssm_imported_values:
108
- vpc_id = self.ssm_imported_values["vpc_id"]
109
-
110
- # Build VPC attributes
111
- vpc_attrs = {
112
- "vpc_id": vpc_id,
113
- "availability_zones": ["us-east-1a", "us-east-1b"]
114
- }
115
-
116
- # Use from_vpc_attributes() for SSM tokens
117
- self._vpc = ec2.Vpc.from_vpc_attributes(self, "VPC", **vpc_attrs)
118
- else:
119
- vpc_id = self.ecs_config.vpc_id or self.workload.vpc_id
120
-
121
- if not vpc_id:
122
- raise ValueError("VPC ID is required for ECS service")
123
-
124
- self._vpc = ec2.Vpc.from_lookup(
125
- self,
126
- "VPC",
127
- vpc_id=vpc_id
128
- )
106
+ """Load VPC using the centralized VPC provider mixin."""
107
+ # Use the centralized VPC resolution from VPCProviderMixin
108
+ self._vpc = self.resolve_vpc(
109
+ config=self.ecs_config,
110
+ deployment=self.deployment,
111
+ workload=self.workload
112
+ )
129
113
 
130
114
  def _process_ssm_imports(self) -> None:
131
115
  """
@@ -25,7 +25,7 @@ from cdk_factory.configurations.deployment import DeploymentConfig
25
25
  from cdk_factory.configurations.stack import StackConfig
26
26
  from cdk_factory.configurations.resources.lambda_edge import LambdaEdgeConfig
27
27
  from cdk_factory.interfaces.istack import IStack
28
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
28
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
29
29
  from cdk_factory.stack.stack_module_registry import register_stack
30
30
  from cdk_factory.workload.workload_factory import WorkloadConfig
31
31
 
@@ -34,7 +34,7 @@ logger = Logger(service="LambdaEdgeStack")
34
34
 
35
35
  @register_stack("lambda_edge_library_module")
36
36
  @register_stack("lambda_edge_stack")
37
- class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
37
+ class LambdaEdgeStack(IStack, StandardizedSsmMixin):
38
38
  """
39
39
  Reusable stack for Lambda@Edge functions.
40
40
 
@@ -19,7 +19,8 @@ from cdk_factory.configurations.deployment import DeploymentConfig
19
19
  from cdk_factory.configurations.stack import StackConfig
20
20
  from cdk_factory.configurations.resources.load_balancer import LoadBalancerConfig
21
21
  from cdk_factory.interfaces.istack import IStack
22
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
22
+ from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
23
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
23
24
  from cdk_factory.stack.stack_module_registry import register_stack
24
25
  from cdk_factory.workload.workload_factory import WorkloadConfig
25
26
 
@@ -30,7 +31,7 @@ logger = Logger(service="LoadBalancerStack")
30
31
  @register_stack("alb_stack")
31
32
  @register_stack("load_balancer_library_module")
32
33
  @register_stack("load_balancer_stack")
33
- class LoadBalancerStack(IStack, EnhancedSsmParameterMixin):
34
+ class LoadBalancerStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
34
35
  """
35
36
  Reusable stack for AWS Load Balancers.
36
37
  Supports creating Application and Network Load Balancers with customizable configurations.
@@ -174,41 +175,16 @@ class LoadBalancerStack(IStack, EnhancedSsmParameterMixin):
174
175
 
175
176
  @property
176
177
  def vpc(self) -> ec2.IVpc:
177
- """Get the VPC for the Load Balancer"""
178
+ """Get the VPC for the Load Balancer using centralized VPC provider mixin."""
178
179
  if self._vpc:
179
180
  return self._vpc
180
-
181
- # Check SSM imported values first (tokens from SSM parameters)
182
- if "vpc_id" in self.ssm_imported_values:
183
- vpc_id = self.ssm_imported_values["vpc_id"]
184
-
185
- # Build VPC attributes
186
- vpc_attrs = {
187
- "vpc_id": vpc_id,
188
- "availability_zones": ["us-east-1a", "us-east-1b"],
189
- }
190
-
191
- # If we have subnet_ids from SSM, provide dummy public subnets
192
- # The actual subnets will be set via CloudFormation escape hatch
193
- if "subnet_ids" in self.ssm_imported_values:
194
- # Provide dummy subnet IDs - these will be overridden by the escape hatch
195
- # We need at least one dummy subnet per AZ to satisfy CDK's validation
196
- vpc_attrs["public_subnet_ids"] = ["subnet-dummy1", "subnet-dummy2"]
197
-
198
- # Use from_vpc_attributes() instead of from_lookup() because SSM imports return tokens
199
- self._vpc = ec2.Vpc.from_vpc_attributes(self, "VPC", **vpc_attrs)
200
- elif self.lb_config.vpc_id:
201
- self._vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=self.lb_config.vpc_id)
202
- elif self.workload.vpc_id:
203
- self._vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=self.workload.vpc_id)
204
- else:
205
- # Use default VPC if not provided
206
- raise ValueError(
207
- "VPC is not defined in the configuration. "
208
- "You can provide it a the load_balancer.vpc_id in the configuration "
209
- "or a top level workload.vpc_id in the workload configuration."
210
- )
211
-
181
+
182
+ # Use the centralized VPC resolution from VPCProviderMixin
183
+ self._vpc = self.resolve_vpc(
184
+ config=self.lb_config,
185
+ deployment=self.deployment,
186
+ workload=self.workload
187
+ )
212
188
  return self._vpc
213
189
 
214
190
  def _process_ssm_imports(self) -> None:
@@ -18,7 +18,8 @@ from cdk_factory.configurations.deployment import DeploymentConfig
18
18
  from cdk_factory.configurations.stack import StackConfig
19
19
  from cdk_factory.configurations.resources.rds import RdsConfig
20
20
  from cdk_factory.interfaces.istack import IStack
21
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
21
+ from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
22
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
22
23
  from cdk_factory.stack.stack_module_registry import register_stack
23
24
  from cdk_factory.workload.workload_factory import WorkloadConfig
24
25
 
@@ -27,7 +28,7 @@ logger = Logger(service="RdsStack")
27
28
 
28
29
  @register_stack("rds_library_module")
29
30
  @register_stack("rds_stack")
30
- class RdsStack(IStack, EnhancedSsmParameterMixin):
31
+ class RdsStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
31
32
  """
32
33
  Reusable stack for AWS RDS.
33
34
  Supports creating RDS instances with customizable configurations.
@@ -115,34 +116,16 @@ class RdsStack(IStack, EnhancedSsmParameterMixin):
115
116
 
116
117
  @property
117
118
  def vpc(self) -> ec2.IVpc:
118
- """Get the VPC for the RDS instance"""
119
+ """Get the VPC for the RDS instance using centralized VPC provider mixin."""
119
120
  if self._vpc:
120
121
  return self._vpc
121
122
 
122
- # Check SSM imported values first (tokens from SSM parameters)
123
- if "vpc_id" in self.ssm_imported_values:
124
- vpc_id = self.ssm_imported_values["vpc_id"]
125
-
126
- # When using tokens, we can't provide subnet lists to from_vpc_attributes
127
- # because CDK validates subnet count against AZ count at synthesis time
128
- # We'll create a DB subnet group separately instead
129
- vpc_attrs = {
130
- "vpc_id": vpc_id,
131
- "availability_zones": ["us-east-1a", "us-east-1b"]
132
- }
133
-
134
- # Use from_vpc_attributes() for SSM tokens
135
- self._vpc = ec2.Vpc.from_vpc_attributes(self, "VPC", **vpc_attrs)
136
- elif self.rds_config.vpc_id:
137
- self._vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=self.rds_config.vpc_id)
138
- elif self.workload.vpc_id:
139
- self._vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=self.workload.vpc_id)
140
- else:
141
- raise ValueError(
142
- "VPC is not defined in the configuration. "
143
- "You can provide it a the rds.vpc_id in the configuration "
144
- "or a top level workload.vpc_id in the workload configuration."
145
- )
123
+ # Use the centralized VPC resolution from VPCProviderMixin
124
+ self._vpc = self.resolve_vpc(
125
+ config=self.rds_config,
126
+ deployment=self.deployment,
127
+ workload=self.workload
128
+ )
146
129
  return self._vpc
147
130
 
148
131
  def _get_security_groups(self) -> List[ec2.ISecurityGroup]:
@@ -18,7 +18,7 @@ from cdk_factory.configurations.deployment import DeploymentConfig
18
18
  from cdk_factory.configurations.stack import StackConfig
19
19
  from cdk_factory.configurations.resources.route53 import Route53Config
20
20
  from cdk_factory.interfaces.istack import IStack
21
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
21
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
22
22
  from cdk_factory.stack.stack_module_registry import register_stack
23
23
  from cdk_factory.workload.workload_factory import WorkloadConfig
24
24
 
@@ -27,7 +27,7 @@ logger = Logger(service="Route53Stack")
27
27
 
28
28
  @register_stack("route53_library_module")
29
29
  @register_stack("route53_stack")
30
- class Route53Stack(IStack, EnhancedSsmParameterMixin):
30
+ class Route53Stack(IStack, StandardizedSsmMixin):
31
31
  """
32
32
  Reusable stack for AWS Route53.
33
33
  Supports creating hosted zones, DNS records, and certificate validation.