cdk-factory 0.16.15__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.15.dist-info → cdk_factory-0.17.0.dist-info}/METADATA +1 -1
  49. {cdk_factory-0.16.15.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 -721
  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.15.dist-info → cdk_factory-0.17.0.dist-info}/WHEEL +0 -0
  56. {cdk_factory-0.16.15.dist-info → cdk_factory-0.17.0.dist-info}/entry_points.txt +0 -0
  57. {cdk_factory-0.16.15.dist-info → cdk_factory-0.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -16,7 +16,7 @@ from cdk_factory.configurations.deployment import DeploymentConfig
16
16
  from cdk_factory.configurations.resources.rum import RumConfig
17
17
  from cdk_factory.configurations.stack import StackConfig
18
18
  from cdk_factory.interfaces.istack import IStack
19
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
19
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
20
20
  from cdk_factory.stack.stack_module_registry import register_stack
21
21
  from cdk_factory.workload.workload_factory import WorkloadConfig
22
22
 
@@ -25,7 +25,7 @@ logger = Logger(__name__)
25
25
 
26
26
  @register_stack("rum_library_module")
27
27
  @register_stack("rum_stack")
28
- class RumStack(IStack, EnhancedSsmParameterMixin):
28
+ class RumStack(IStack, StandardizedSsmMixin):
29
29
  """
30
30
  RUM Stack - Creates a CloudWatch RUM app monitor with optional Cognito integration.
31
31
  Can either use existing Cognito resources or create new ones if not provided.
@@ -51,15 +51,28 @@ class RumStack(IStack, EnhancedSsmParameterMixin):
51
51
  self.stack_config = stack_config
52
52
  self.deployment = deployment
53
53
  self.rum_config = RumConfig(stack_config.dictionary.get("rum", {}))
54
-
54
+
55
55
  logger.info(f"Building RUM stack: {self.rum_config.name}")
56
56
 
57
57
  # Setup enhanced SSM integration
58
- self.setup_enhanced_ssm_integration(
58
+ rum_config = stack_config.dictionary.get("rum", {}).copy()
59
+
60
+ # Configure SSM imports for cognito resources if needed
61
+ if not self.rum_config.cognito_identity_pool_id:
62
+ # Add explicit import path for cognito identity pool using new pattern
63
+ if "ssm" not in rum_config:
64
+ rum_config["ssm"] = {}
65
+ if "imports" not in rum_config["ssm"]:
66
+ rum_config["ssm"]["imports"] = {}
67
+ rum_config["ssm"]["imports"][
68
+ "cognito_identity_pool_id"
69
+ ] = "/{{ORGANIZATION}}/{{ENVIRONMENT}}/cognito/user-pool/identity-pool-id"
70
+
71
+ self.setup_standardized_ssm_integration(
59
72
  scope=self,
60
- config=stack_config.dictionary.get("rum", {}),
73
+ config=rum_config,
61
74
  resource_type="rum",
62
- resource_name=self.rum_config.name
75
+ resource_name=self.rum_config.name,
63
76
  )
64
77
 
65
78
  # Import or create Cognito resources
@@ -84,14 +97,15 @@ class RumStack(IStack, EnhancedSsmParameterMixin):
84
97
  identity_pool_id = self.rum_config.cognito_identity_pool_id
85
98
  logger.info(f"Using existing Cognito Identity Pool: {identity_pool_id}")
86
99
  else:
87
- # Try to import from SSM using enhanced SSM pattern
88
- imported_values = self.auto_import_resources({
89
- "cognito_identity_pool_id": "auto"
90
- })
91
-
92
- if imported_values.get("cognito_identity_pool_id"):
93
- identity_pool_id = imported_values["cognito_identity_pool_id"]
94
- logger.info(f"Imported Cognito Identity Pool from SSM: {identity_pool_id}")
100
+ # Try to import from SSM using new pattern - read directly from config
101
+ ssm_imports = self.rum_config.ssm_imports
102
+ cognito_identity_pool_id = ssm_imports.get("cognito_identity_pool_id")
103
+
104
+ if cognito_identity_pool_id:
105
+ identity_pool_id = cognito_identity_pool_id
106
+ logger.info(
107
+ f"Imported Cognito Identity Pool from SSM: {identity_pool_id}"
108
+ )
95
109
 
96
110
  # If no existing identity pool found, create new Cognito resources
97
111
  if not identity_pool_id and self.rum_config.create_cognito_identity_pool:
@@ -107,17 +121,21 @@ class RumStack(IStack, EnhancedSsmParameterMixin):
107
121
  """Create new Cognito User Pool and Identity Pool for RUM"""
108
122
  logger.info("Creating new Cognito resources for RUM")
109
123
 
124
+ def _resource_name(name: str) -> str:
125
+ """Helper to generate resource names"""
126
+ return f"{self.rum_config.name}-{name}"
127
+
110
128
  # Create User Pool if needed
111
129
  user_pool_id = self.rum_config.cognito_user_pool_id
112
130
  if not user_pool_id and self.rum_config.create_cognito_user_pool:
113
131
  self.user_pool = cognito.UserPool(
114
132
  self,
115
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-user-pool"),
133
+ id=_resource_name("user-pool"),
116
134
  user_pool_name=self.rum_config.cognito_user_pool_name,
117
135
  self_sign_up_enabled=True,
118
136
  sign_in_aliases=cognito.SignInAliases(email=True),
119
137
  auto_verify=cognito.AutoVerifiedAttrs(email=True),
120
- removal_policy=cdk.RemovalPolicy.DESTROY
138
+ removal_policy=cdk.RemovalPolicy.DESTROY,
121
139
  )
122
140
  user_pool_id = self.user_pool.user_pool_id
123
141
  logger.info(f"Created Cognito User Pool: {user_pool_id}")
@@ -128,33 +146,34 @@ class RumStack(IStack, EnhancedSsmParameterMixin):
128
146
  # Create User Pool Client for Identity Pool integration
129
147
  user_pool_client = cognito.UserPoolClient(
130
148
  self,
131
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-user-pool-client"),
149
+ id=_resource_name("user-pool-client"),
132
150
  user_pool=self.user_pool,
133
151
  generate_secret=False,
134
- auth_flows=cognito.AuthFlow(
135
- user_srp=True,
136
- user_password=True
137
- )
152
+ auth_flows=cognito.AuthFlow(user_srp=True, user_password=True),
153
+ )
154
+
155
+ identity_pool_providers.append(
156
+ {
157
+ "providerName": f"cognito-idp.{self.region}.amazonaws.com/{user_pool_id}",
158
+ "providerType": "COGNITO_USER_POOLS",
159
+ "clientId": user_pool_client.user_pool_client_id,
160
+ }
138
161
  )
139
-
140
- identity_pool_providers.append({
141
- "providerName": f"cognito-idp.{self.region}.amazonaws.com/{user_pool_id}",
142
- "providerType": "COGNITO_USER_POOLS",
143
- "clientId": user_pool_client.user_pool_client_id
144
- })
145
162
 
146
163
  self.identity_pool = cognito.CfnIdentityPool(
147
164
  self,
148
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-identity-pool"),
165
+ id=_resource_name("identity-pool"),
149
166
  identity_pool_name=self.rum_config.cognito_identity_pool_name,
150
167
  allow_unauthenticated_identities=True,
151
- cognito_identity_providers=identity_pool_providers if identity_pool_providers else None
168
+ cognito_identity_providers=(
169
+ identity_pool_providers if identity_pool_providers else None
170
+ ),
152
171
  )
153
172
 
154
173
  # Create IAM role for unauthenticated users (guest role)
155
174
  guest_role = iam.Role(
156
175
  self,
157
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-guest-role"),
176
+ id=_resource_name("guest-role"),
158
177
  assumed_by=iam.FederatedPrincipal(
159
178
  "cognito-identity.amazonaws.com",
160
179
  conditions={
@@ -163,52 +182,55 @@ class RumStack(IStack, EnhancedSsmParameterMixin):
163
182
  },
164
183
  "ForAnyValue:StringLike": {
165
184
  "cognito-identity.amazonaws.com:amr": "unauthenticated"
166
- }
167
- }
185
+ },
186
+ },
168
187
  ),
169
188
  inline_policies={
170
189
  "RUMPolicy": iam.PolicyDocument(
171
190
  statements=[
172
191
  iam.PolicyStatement(
173
192
  effect=iam.Effect.ALLOW,
174
- actions=[
175
- "rum:PutRumEvents"
193
+ actions=["rum:PutRumEvents"],
194
+ resources=[
195
+ f"arn:aws:rum:{self.region}:{self.account}:appmonitor/{self.rum_config.name}"
176
196
  ],
177
- resources=[f"arn:aws:rum:{self.region}:{self.account}:appmonitor/{self.rum_config.name}"]
178
197
  )
179
198
  ]
180
199
  )
181
- }
200
+ },
182
201
  )
183
202
 
184
203
  # Attach the role to the identity pool
185
204
  cognito.CfnIdentityPoolRoleAttachment(
186
205
  self,
187
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-role-attachment"),
206
+ id=_resource_name("role-attachment"),
188
207
  identity_pool_id=self.identity_pool.ref,
189
- roles={
190
- "unauthenticated": guest_role.role_arn
191
- }
208
+ roles={"unauthenticated": guest_role.role_arn},
192
209
  )
193
210
 
194
211
  logger.info(f"Created Cognito Identity Pool: {self.identity_pool.ref}")
195
212
  return self.identity_pool.ref, guest_role.role_arn
196
213
 
197
214
  def _create_minimal_identity_pool(self) -> tuple[str, str]:
198
- """Create a minimal identity pool for RUM when no Cognito resources are provided"""
215
+ """Create a minimal Identity Pool with just a guest role for RUM"""
199
216
  logger.info("Creating minimal Cognito Identity Pool for RUM")
200
217
 
201
- self.identity_pool = cognito.CfnIdentityPool(
218
+ def _resource_name(name: str) -> str:
219
+ """Helper to generate resource names"""
220
+ return f"{self.rum_config.name}-{name}"
221
+
222
+ # Create minimal Identity Pool
223
+ minimal_identity_pool = cognito.CfnIdentityPool(
202
224
  self,
203
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-minimal-identity-pool"),
225
+ id=_resource_name("minimal-identity-pool"),
204
226
  identity_pool_name=f"{self.rum_config.name}_minimal_identity_pool",
205
- allow_unauthenticated_identities=True
227
+ allow_unauthenticated_identities=True,
206
228
  )
207
229
 
208
230
  # Create minimal IAM role for unauthenticated users
209
231
  guest_role = iam.Role(
210
232
  self,
211
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-minimal-guest-role"),
233
+ id=_resource_name("minimal-guest-role"),
212
234
  assumed_by=iam.FederatedPrincipal(
213
235
  "cognito-identity.amazonaws.com",
214
236
  conditions={
@@ -217,85 +239,74 @@ class RumStack(IStack, EnhancedSsmParameterMixin):
217
239
  },
218
240
  "ForAnyValue:StringLike": {
219
241
  "cognito-identity.amazonaws.com:amr": "unauthenticated"
220
- }
221
- }
242
+ },
243
+ },
222
244
  ),
223
245
  inline_policies={
224
246
  "RUMPolicy": iam.PolicyDocument(
225
247
  statements=[
226
248
  iam.PolicyStatement(
227
249
  effect=iam.Effect.ALLOW,
228
- actions=[
229
- "rum:PutRumEvents"
250
+ actions=["rum:PutRumEvents"],
251
+ resources=[
252
+ f"arn:aws:rum:{self.region}:{self.account}:appmonitor/{self.rum_config.name}"
230
253
  ],
231
- resources=[f"arn:aws:rum:{self.region}:{self.account}:appmonitor/{self.rum_config.name}"]
232
254
  )
233
255
  ]
234
256
  )
235
- }
257
+ },
236
258
  )
237
259
 
238
260
  # Attach the role to the identity pool
239
261
  cognito.CfnIdentityPoolRoleAttachment(
240
262
  self,
241
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-minimal-role-attachment"),
263
+ id=_resource_name("minimal-role-attachment"),
242
264
  identity_pool_id=self.identity_pool.ref,
243
- roles={
244
- "unauthenticated": guest_role.role_arn
245
- }
265
+ roles={"unauthenticated": guest_role.role_arn},
246
266
  )
247
267
 
248
268
  return self.identity_pool.ref, guest_role.role_arn
249
269
 
250
- def _create_app_monitor(self, identity_pool_id: str, guest_role_arn: Optional[str]) -> None:
270
+ def _create_app_monitor(
271
+ self, identity_pool_id: str, guest_role_arn: Optional[str]
272
+ ) -> None:
251
273
  """Create the CloudWatch RUM app monitor"""
252
- logger.info(f"Creating RUM app monitor: {self.rum_config.name}")
274
+ logger.info("Creating CloudWatch RUM app monitor")
253
275
 
254
- # Build app monitor configuration
276
+ def _resource_name(name: str) -> str:
277
+ """Helper to generate resource names"""
278
+ return f"{self.rum_config.name}-{name}"
279
+
280
+ # Create app monitor configuration
255
281
  app_monitor_config = rum.CfnAppMonitor.AppMonitorConfigurationProperty(
256
- identity_pool_id=identity_pool_id,
257
- guest_role_arn=guest_role_arn,
258
282
  allow_cookies=self.rum_config.allow_cookies,
259
283
  enable_x_ray=self.rum_config.enable_xray,
284
+ favorite_pages=self.rum_config.favorite_pages,
285
+ guest_role_arn=guest_role_arn,
286
+ identity_pool_id=identity_pool_id,
260
287
  session_sample_rate=self.rum_config.session_sample_rate,
261
- telemetries=self.rum_config.telemetries
288
+ telemetries=self.rum_config.telemetries,
262
289
  )
263
290
 
264
- # Add optional properties
265
- if self.rum_config.excluded_pages:
266
- app_monitor_config.excluded_pages = self.rum_config.excluded_pages
267
-
268
- if self.rum_config.included_pages:
269
- app_monitor_config.included_pages = self.rum_config.included_pages
270
-
271
- if self.rum_config.favorite_pages:
272
- app_monitor_config.favorite_pages = self.rum_config.favorite_pages
273
-
274
- if self.rum_config.metric_destinations:
275
- app_monitor_config.metric_destinations = [
276
- rum.CfnAppMonitor.MetricDestinationProperty(**dest)
277
- for dest in self.rum_config.metric_destinations
278
- ]
279
-
280
- # Create custom events configuration if enabled
281
- custom_events = None
282
- if self.rum_config.custom_events_enabled:
283
- custom_events = rum.CfnAppMonitor.CustomEventsProperty(
284
- status="ENABLED"
285
- )
286
-
287
- # Create the app monitor
288
291
  self.app_monitor = rum.CfnAppMonitor(
289
292
  self,
290
- id=self.deployment.build_resource_name(f"{self.rum_config.name}-app-monitor"),
293
+ id=_resource_name("app-monitor"),
291
294
  name=self.rum_config.name,
292
- app_monitor_configuration=app_monitor_config,
293
295
  domain=self.rum_config.domain,
294
- domain_list=self.rum_config.domain_list,
296
+ app_monitor_configuration=app_monitor_config,
295
297
  cw_log_enabled=self.rum_config.cw_log_enabled,
296
- custom_events=custom_events
297
298
  )
298
299
 
300
+ logger.info(f"Created CloudWatch RUM app monitor: {self.rum_config.name}")
301
+
302
+ # Create custom events configuration if enabled
303
+ custom_events = None
304
+ if self.rum_config.custom_events_enabled:
305
+ custom_events = rum.CfnAppMonitor.CustomEventsProperty(status="ENABLED")
306
+
307
+ # Update the app monitor with additional properties
308
+ # (Note: some properties like custom_events need to be set during creation)
309
+
299
310
  # Add tags if specified
300
311
  if self.rum_config.tags:
301
312
  for key, value in self.rum_config.tags.items():
@@ -324,8 +335,8 @@ class RumStack(IStack, EnhancedSsmParameterMixin):
324
335
  resource_values["user_pool_id"] = self.user_pool.user_pool_id
325
336
 
326
337
  # Use enhanced SSM parameter export
327
- exported_params = self.auto_export_resources(resource_values)
328
-
338
+ exported_params = self.export_standardized_ssm_parameters(resource_values)
339
+
329
340
  if exported_params:
330
341
  logger.info(f"Exported {len(exported_params)} RUM parameters to SSM")
331
342
  else:
@@ -11,6 +11,7 @@ from cdk_factory.configurations.resources.security_group_full_stack import (
11
11
  SecurityGroupFullStackConfig,
12
12
  )
13
13
  from cdk_factory.interfaces.istack import IStack
14
+ from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
14
15
  from cdk_factory.stack.stack_module_registry import register_stack
15
16
  from cdk_factory.workload.workload_factory import WorkloadConfig
16
17
 
@@ -19,7 +20,7 @@ logger = Logger(service="SecurityGroupFullStack")
19
20
 
20
21
  @register_stack("security_group_full_stack_library_module")
21
22
  @register_stack("security_group_full_stack")
22
- class SecurityGroupsStack(IStack):
23
+ class SecurityGroupsStack(IStack, VPCProviderMixin):
23
24
 
24
25
  def __init__(self, scope: Construct, id: str, **kwargs) -> None:
25
26
  super().__init__(scope, id, **kwargs)
@@ -274,30 +275,16 @@ class SecurityGroupsStack(IStack):
274
275
 
275
276
  @property
276
277
  def vpc(self) -> ec2.IVpc:
277
- """Get the VPC for the Security Group"""
278
+ """Get the VPC for the Security Group using centralized VPC provider mixin."""
278
279
  if self._vpc:
279
280
  return self._vpc
280
281
 
281
- # Check SSM imported values first (tokens from SSM parameters)
282
- if "vpc_id" in self.ssm_imported_values:
283
- vpc_id = self.ssm_imported_values["vpc_id"]
284
-
285
- # Build VPC attributes
286
- vpc_attrs = {
287
- "vpc_id": vpc_id,
288
- "availability_zones": ["us-east-1a", "us-east-1b"]
289
- }
290
-
291
- # Use from_vpc_attributes() instead of from_lookup() because SSM imports return tokens
292
- # from_lookup() requires concrete values and queries AWS during synthesis
293
- self._vpc = ec2.Vpc.from_vpc_attributes(self, "VPC", **vpc_attrs)
294
- elif self.sg_config.vpc_id:
295
- self._vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=self.sg_config.vpc_id)
296
- elif self.workload.vpc_id:
297
- self._vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=self.workload.vpc_id)
298
- else:
299
- raise ValueError("VPC ID is not defined in the configuration or SSM imports.")
300
-
282
+ # Use the centralized VPC resolution from VPCProviderMixin
283
+ self._vpc = self.resolve_vpc(
284
+ config=self.sg_config,
285
+ deployment=self.deployment,
286
+ workload=self.workload
287
+ )
301
288
  return self._vpc
302
289
 
303
290
  def _export_ssm_parameters(self, security_groups_map: Dict[str, ec2.CfnSecurityGroup]) -> None:
@@ -15,7 +15,8 @@ from cdk_factory.configurations.deployment import DeploymentConfig
15
15
  from cdk_factory.configurations.stack import StackConfig
16
16
  from cdk_factory.configurations.resources.security_group import SecurityGroupConfig
17
17
  from cdk_factory.interfaces.istack import IStack
18
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
18
+ from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
19
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
19
20
  from cdk_factory.stack.stack_module_registry import register_stack
20
21
  from cdk_factory.workload.workload_factory import WorkloadConfig
21
22
 
@@ -24,7 +25,7 @@ logger = Logger(service="SecurityGroupStack")
24
25
 
25
26
  @register_stack("security_group_library_module")
26
27
  @register_stack("security_group_stack")
27
- class SecurityGroupStack(IStack, EnhancedSsmParameterMixin):
28
+ class SecurityGroupStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
28
29
  """
29
30
  Reusable stack for AWS Security Groups.
30
31
  Supports creating security groups with customizable rules.
@@ -82,17 +83,16 @@ class SecurityGroupStack(IStack, EnhancedSsmParameterMixin):
82
83
 
83
84
  @property
84
85
  def vpc(self) -> ec2.IVpc:
85
- """Get the VPC for the Security Group"""
86
+ """Get the VPC for the Security Group using centralized VPC provider mixin."""
86
87
  if self._vpc:
87
88
  return self._vpc
88
- if self.sg_config.vpc_id:
89
- self._vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=self.sg_config.vpc_id)
90
- elif self.workload.vpc_id:
91
- self._vpc = ec2.Vpc.from_lookup(self, "VPC", vpc_id=self.workload.vpc_id)
92
-
93
- else:
94
- raise ValueError("VPC ID is not defined in the configuration.")
95
-
89
+
90
+ # Use the centralized VPC resolution from VPCProviderMixin
91
+ self._vpc = self.resolve_vpc(
92
+ config=self.sg_config,
93
+ deployment=self.deployment,
94
+ workload=self.workload
95
+ )
96
96
  return self._vpc
97
97
 
98
98
  def _create_security_group(self, sg_name: str) -> ec2.SecurityGroup: