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,411 @@
1
+ """
2
+ VPC Stack Pattern for CDK-Factory (Standardized SSM Version)
3
+ Maintainers: Eric Wilson
4
+ MIT License. See Project Root for the license information.
5
+ """
6
+
7
+ from typing import Dict, Any, List, Optional
8
+
9
+ import aws_cdk as cdk
10
+ from aws_cdk import aws_ec2 as ec2
11
+ from aws_lambda_powertools import Logger
12
+ from constructs import Construct
13
+
14
+ from cdk_factory.configurations.deployment import DeploymentConfig
15
+ from cdk_factory.configurations.stack import StackConfig
16
+ from cdk_factory.configurations.resources.vpc import VpcConfig
17
+ from cdk_factory.interfaces.istack import IStack
18
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
19
+ from cdk_factory.stack.stack_module_registry import register_stack
20
+ from cdk_factory.workload.workload_factory import WorkloadConfig
21
+
22
+ logger = Logger(service="VpcStackStandardized")
23
+
24
+
25
+ @register_stack("vpc_library_module")
26
+ @register_stack("vpc_stack")
27
+ class VpcStack(IStack, StandardizedSsmMixin):
28
+ """
29
+ Reusable stack for AWS VPC with standardized SSM integration.
30
+
31
+ This version uses the StandardizedSsmMixin to provide consistent SSM parameter
32
+ handling across all CDK Factory modules.
33
+
34
+ Key Features:
35
+ - Standardized SSM import/export patterns
36
+ - Template variable resolution
37
+ - Comprehensive validation
38
+ - Clear error handling
39
+ - Backward compatibility
40
+ """
41
+
42
+ def __init__(self, scope: Construct, id: str, **kwargs) -> None:
43
+ # Initialize parent classes properly
44
+ super().__init__(scope, id, **kwargs)
45
+
46
+ # Initialize module attributes
47
+ self.vpc_config = None
48
+ self.stack_config = None
49
+ self.deployment = None
50
+ self.workload = None
51
+ self.vpc = None
52
+
53
+ def build(
54
+ self,
55
+ stack_config: StackConfig,
56
+ deployment: DeploymentConfig,
57
+ workload: WorkloadConfig,
58
+ ) -> None:
59
+ """Build the VPC stack"""
60
+ self._build(stack_config, deployment, workload)
61
+
62
+ def _build(
63
+ self,
64
+ stack_config: StackConfig,
65
+ deployment: DeploymentConfig,
66
+ workload: WorkloadConfig,
67
+ ) -> None:
68
+ """Internal build method for the VPC stack"""
69
+ self.stack_config = stack_config
70
+ self.deployment = deployment
71
+ self.workload = workload
72
+
73
+ self.vpc_config = VpcConfig(stack_config.dictionary.get("vpc", {}), deployment)
74
+ vpc_name = deployment.build_resource_name(self.vpc_config.name)
75
+
76
+ # Setup standardized SSM integration
77
+ self.setup_standardized_ssm_integration(
78
+ scope=self,
79
+ config=self.vpc_config,
80
+ resource_type="vpc",
81
+ resource_name=vpc_name,
82
+ deployment=deployment,
83
+ workload=workload
84
+ )
85
+
86
+ # Process SSM imports using standardized method
87
+ self.process_standardized_ssm_imports()
88
+
89
+ # Import any required resources from SSM
90
+ imported_resources = self.get_all_ssm_imports()
91
+
92
+ if imported_resources:
93
+ logger.info(f"Imported resources from SSM: {list(imported_resources.keys())}")
94
+
95
+ # Create the VPC
96
+ self.vpc = self._create_vpc(vpc_name)
97
+
98
+ # Add outputs
99
+ self._add_outputs(vpc_name)
100
+
101
+ # Export SSM parameters
102
+ self._export_ssm_parameters()
103
+
104
+ logger.info(f"VPC {vpc_name} built successfully")
105
+
106
+ def _create_vpc(self, vpc_name: str) -> ec2.Vpc:
107
+ """Create a VPC with the specified configuration"""
108
+ # Configure subnet configuration
109
+ subnet_configuration = self._get_subnet_configuration()
110
+
111
+ # Configure NAT gateways
112
+ nat_gateway_count = self.vpc_config.nat_gateways.get("count", 1)
113
+
114
+ # Get explicit availability zones to avoid dummy AZs in pipeline synthesis
115
+ # When CDK synthesizes in a pipeline context, it doesn't have access to real AZs
116
+ # So we explicitly specify them based on the deployment region
117
+ availability_zones = None
118
+ if self.deployment:
119
+ region = self.deployment.region or "us-east-1"
120
+ # Explicitly list AZs for the region to avoid dummy values
121
+ max_azs = self.vpc_config.max_azs or 2
122
+ if region == "us-east-1":
123
+ availability_zones = [f"us-east-1{chr(97+i)}" for i in range(max_azs)] # us-east-1a, us-east-1b, etc.
124
+ elif region == "us-east-2":
125
+ availability_zones = [f"us-east-2{chr(97+i)}" for i in range(max_azs)]
126
+ elif region == "us-west-1":
127
+ availability_zones = [f"us-west-1{chr(97+i)}" for i in range(max_azs)]
128
+ elif region == "us-west-2":
129
+ availability_zones = [f"us-west-2{chr(97+i)}" for i in range(max_azs)]
130
+
131
+ # Build VPC properties
132
+ # Note: CDK doesn't allow both 'availability_zones' and 'max_azs' - use one or the other
133
+ vpc_props = {
134
+ "vpc_name": vpc_name,
135
+ "cidr": self.vpc_config.cidr,
136
+ "nat_gateways": nat_gateway_count,
137
+ "subnet_configuration": subnet_configuration,
138
+ "enable_dns_hostnames": self.vpc_config.enable_dns_hostnames,
139
+ "enable_dns_support": self.vpc_config.enable_dns_support,
140
+ "gateway_endpoints": (
141
+ {
142
+ "S3": ec2.GatewayVpcEndpointOptions(
143
+ service=ec2.GatewayVpcEndpointAwsService.S3
144
+ )
145
+ }
146
+ if self.vpc_config.enable_s3_endpoint
147
+ else None
148
+ ),
149
+ }
150
+
151
+ # Use either availability_zones or max_azs, not both
152
+ if availability_zones:
153
+ vpc_props["availability_zones"] = availability_zones
154
+ else:
155
+ vpc_props["max_azs"] = self.vpc_config.max_azs
156
+
157
+ # Create the VPC
158
+ vpc = ec2.Vpc(self, vpc_name, **vpc_props)
159
+
160
+ # Add interface endpoints if specified
161
+ if self.vpc_config.enable_interface_endpoints:
162
+ self._add_interface_endpoints(vpc, self.vpc_config.interface_endpoints)
163
+
164
+ # Add tags if specified
165
+ for key, value in self.vpc_config.tags.items():
166
+ cdk.Tags.of(vpc).add(key, value)
167
+
168
+ return vpc
169
+
170
+ def _get_subnet_configuration(self) -> List[ec2.SubnetConfiguration]:
171
+ """Configure the subnets for the VPC"""
172
+ subnet_configs = []
173
+
174
+ # Public subnets
175
+ if self.vpc_config.subnets.get("public", {}).get("enabled", True):
176
+ public_config = self.vpc_config.subnets["public"]
177
+ subnet_configs.append(
178
+ ec2.SubnetConfiguration(
179
+ name=self.vpc_config.public_subnet_name,
180
+ subnet_type=ec2.SubnetType.PUBLIC,
181
+ cidr_mask=public_config.get("cidr_mask", 24),
182
+ map_public_ip_on_launch=public_config.get("map_public_ip", True),
183
+ )
184
+ )
185
+
186
+ # Private subnets
187
+ if self.vpc_config.subnets.get("private", {}).get("enabled", True):
188
+ private_config = self.vpc_config.subnets["private"]
189
+ subnet_configs.append(
190
+ ec2.SubnetConfiguration(
191
+ name=self.vpc_config.private_subnet_name,
192
+ subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS,
193
+ cidr_mask=private_config.get("cidr_mask", 24),
194
+ )
195
+ )
196
+
197
+ # Isolated subnets
198
+ if self.vpc_config.subnets.get("isolated", {}).get("enabled", False):
199
+ isolated_config = self.vpc_config.subnets["isolated"]
200
+ subnet_configs.append(
201
+ ec2.SubnetConfiguration(
202
+ name=self.vpc_config.isolated_subnet_name,
203
+ subnet_type=ec2.SubnetType.PRIVATE_ISOLATED,
204
+ cidr_mask=isolated_config.get("cidr_mask", 24),
205
+ )
206
+ )
207
+
208
+ return subnet_configs
209
+
210
+ def _add_interface_endpoints(self, vpc: ec2.Vpc, endpoints: List[str]) -> None:
211
+ """Add VPC interface endpoints"""
212
+ endpoint_services = {
213
+ "ecr.api": ec2.InterfaceVpcEndpointAwsService.ECR,
214
+ "ecr.dkr": ec2.InterfaceVpcEndpointAwsService.ECR_DOCKER,
215
+ "ec2": ec2.InterfaceVpcEndpointAwsService.EC2,
216
+ "ecs": ec2.InterfaceVpcEndpointAwsService.ECS,
217
+ "lambda": ec2.InterfaceVpcEndpointAwsService.LAMBDA,
218
+ "secretsmanager": ec2.InterfaceVpcEndpointAwsService.SECRETS_MANAGER,
219
+ "ssm": ec2.InterfaceVpcEndpointAwsService.SSM,
220
+ "kms": ec2.InterfaceVpcEndpointAwsService.KMS,
221
+ }
222
+
223
+ for endpoint_name in endpoints:
224
+ if endpoint_name in endpoint_services:
225
+ vpc.add_interface_endpoint(
226
+ f"{endpoint_name}-endpoint",
227
+ service=endpoint_services[endpoint_name],
228
+ subnets=ec2.SubnetSelection(
229
+ subnet_type=ec2.SubnetType.PRIVATE_WITH_EGRESS
230
+ ),
231
+ )
232
+ logger.info(f"Added interface endpoint: {endpoint_name}")
233
+ else:
234
+ logger.warning(f"Unknown interface endpoint: {endpoint_name}")
235
+
236
+ def _add_outputs(self, vpc_name: str) -> None:
237
+ """Add CloudFormation outputs for the VPC"""
238
+ if not self.vpc:
239
+ return
240
+
241
+ # VPC outputs
242
+ cdk.CfnOutput(
243
+ self,
244
+ f"{vpc_name}-VpcId",
245
+ value=self.vpc.vpc_id,
246
+ description=f"VPC ID for {vpc_name}",
247
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-vpc-id",
248
+ )
249
+
250
+ # Subnet outputs
251
+ public_subnet_ids = [subnet.subnet_id for subnet in self.vpc.public_subnets]
252
+ if public_subnet_ids:
253
+ cdk.CfnOutput(
254
+ self,
255
+ f"{vpc_name}-PublicSubnetIds",
256
+ value=",".join(public_subnet_ids),
257
+ description=f"Public subnet IDs for {vpc_name}",
258
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-public-subnet-ids",
259
+ )
260
+
261
+ private_subnet_ids = [subnet.subnet_id for subnet in self.vpc.private_subnets]
262
+ if private_subnet_ids:
263
+ cdk.CfnOutput(
264
+ self,
265
+ f"{vpc_name}-PrivateSubnetIds",
266
+ value=",".join(private_subnet_ids),
267
+ description=f"Private subnet IDs for {vpc_name}",
268
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-private-subnet-ids",
269
+ )
270
+
271
+ isolated_subnet_ids = [subnet.subnet_id for subnet in self.vpc.isolated_subnets]
272
+ if isolated_subnet_ids:
273
+ cdk.CfnOutput(
274
+ self,
275
+ f"{vpc_name}-IsolatedSubnetIds",
276
+ value=",".join(isolated_subnet_ids),
277
+ description=f"Isolated subnet IDs for {vpc_name}",
278
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-isolated-subnet-ids",
279
+ )
280
+
281
+ # Route table outputs - simplified to avoid route table access issues
282
+ # Skip route table outputs for now as they're causing CDK API issues
283
+ # public_route_table_ids = []
284
+ # if self.vpc.public_subnets:
285
+ # for subnet in self.vpc.public_subnets:
286
+ # # Access route table through the subnet's route table association
287
+ # for association in subnet.node.children:
288
+ # if hasattr(association, 'route_table_id') and association.route_table_id:
289
+ # public_route_table_ids.append(association.route_table_id)
290
+ #
291
+ # if public_route_table_ids:
292
+ # cdk.CfnOutput(
293
+ # self,
294
+ # f"{vpc_name}-PublicRouteTableIds",
295
+ # value=",".join(public_route_table_ids),
296
+ # description=f"Public route table IDs for {vpc_name}",
297
+ # export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-public-route-table-ids",
298
+ # )
299
+ #
300
+ # private_route_table_ids = []
301
+ # if self.vpc.private_subnets:
302
+ # for subnet in self.vpc.private_subnets:
303
+ # # Access route table through the subnet's route table association
304
+ # for association in subnet.node.children:
305
+ # if hasattr(association, 'route_table_id') and association.route_table_id:
306
+ # private_route_table_ids.append(association.route_table_id)
307
+ #
308
+ # if private_route_table_ids:
309
+ # cdk.CfnOutput(
310
+ # self,
311
+ # f"{vpc_name}-PrivateRouteTableIds",
312
+ # value=",".join(private_route_table_ids),
313
+ # description=f"Private route table IDs for {vpc_name}",
314
+ # export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-private-route-table-ids",
315
+ # )
316
+
317
+ # Internet Gateway output
318
+ if hasattr(self.vpc, 'internet_gateway_id') and self.vpc.internet_gateway_id:
319
+ cdk.CfnOutput(
320
+ self,
321
+ f"{vpc_name}-InternetGatewayId",
322
+ value=self.vpc.internet_gateway_id,
323
+ description=f"Internet Gateway ID for {vpc_name}",
324
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-internet-gateway-id",
325
+ )
326
+
327
+ # NAT Gateway outputs - simplified to avoid None values
328
+ nat_gateway_ids = []
329
+ for subnet in self.vpc.public_subnets:
330
+ if hasattr(subnet, 'node') and subnet.node:
331
+ for child in subnet.node.children:
332
+ if hasattr(child, 'nat_gateway_id') and child.nat_gateway_id:
333
+ nat_gateway_ids.append(child.nat_gateway_id)
334
+
335
+ if nat_gateway_ids:
336
+ cdk.CfnOutput(
337
+ self,
338
+ f"{vpc_name}-NatGatewayIds",
339
+ value=",".join(nat_gateway_ids),
340
+ description=f"NAT Gateway IDs for {vpc_name}",
341
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-nat-gateway-ids",
342
+ )
343
+
344
+ def _export_ssm_parameters(self) -> None:
345
+ """Export SSM parameters using standardized approach"""
346
+ if not self.vpc:
347
+ logger.warning("No VPC to export")
348
+ return
349
+
350
+ # Prepare resource values for export
351
+ resource_values = {
352
+ "vpc_id": self.vpc.vpc_id,
353
+ "public_subnet_ids": [subnet.subnet_id for subnet in self.vpc.public_subnets],
354
+ "private_subnet_ids": [subnet.subnet_id for subnet in self.vpc.private_subnets],
355
+ "isolated_subnet_ids": [subnet.subnet_id for subnet in self.vpc.isolated_subnets],
356
+ }
357
+
358
+ # Add route table IDs if available - commented out due to CDK API issues
359
+ # public_route_table_ids = []
360
+ # if self.vpc.public_subnets:
361
+ # for subnet in self.vpc.public_subnets:
362
+ # # Access route table through the subnet's route table association
363
+ # for association in subnet.node.children:
364
+ # if hasattr(association, 'route_table_id') and association.route_table_id:
365
+ # public_route_table_ids.append(association.route_table_id)
366
+ #
367
+ # if public_route_table_ids:
368
+ # resource_values["public_route_table_ids"] = public_route_table_ids
369
+ #
370
+ # private_route_table_ids = []
371
+ # if self.vpc.private_subnets:
372
+ # for subnet in self.vpc.private_subnets:
373
+ # # Access route table through the subnet's route table association
374
+ # for association in subnet.node.children:
375
+ # if hasattr(association, 'route_table_id') and association.route_table_id:
376
+ # private_route_table_ids.append(association.route_table_id)
377
+ #
378
+ # if private_route_table_ids:
379
+ # resource_values["private_route_table_ids"] = private_route_table_ids
380
+
381
+ # Add NAT Gateway IDs if available - simplified to avoid None values
382
+ nat_gateway_ids = []
383
+ for subnet in self.vpc.public_subnets:
384
+ if hasattr(subnet, 'node') and subnet.node:
385
+ for child in subnet.node.children:
386
+ if hasattr(child, 'nat_gateway_id') and child.nat_gateway_id:
387
+ nat_gateway_ids.append(child.nat_gateway_id)
388
+ if nat_gateway_ids:
389
+ resource_values["nat_gateway_ids"] = nat_gateway_ids
390
+
391
+ # Add Internet Gateway ID if available
392
+ if hasattr(self.vpc, 'internet_gateway_id') and self.vpc.internet_gateway_id:
393
+ resource_values["internet_gateway_id"] = self.vpc.internet_gateway_id
394
+
395
+ # Export using standardized SSM mixin
396
+ exported_params = self.export_standardized_ssm_parameters(resource_values)
397
+
398
+ logger.info(f"Exported SSM parameters: {exported_params}")
399
+
400
+ # Backward compatibility methods
401
+ def auto_export_resources(self, resource_values: Dict[str, Any], context: Dict[str, Any] = None) -> Dict[str, str]:
402
+ """Backward compatibility method for existing modules."""
403
+ return self.export_standardized_ssm_parameters(resource_values)
404
+
405
+ def auto_import_resources(self, context: Dict[str, Any] = None) -> Dict[str, Any]:
406
+ """Backward compatibility method for existing modules."""
407
+ return self.get_all_ssm_imports()
408
+
409
+
410
+ # Backward compatibility alias
411
+ VpcStackStandardized = VpcStack
@@ -537,34 +537,42 @@ class ApiGatewayIntegrationUtility:
537
537
 
538
538
  if ssm_path:
539
539
  # Use enhanced SSM parameter import with auto-discovery support
540
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import (
541
- EnhancedSsmParameterMixin,
540
+ from cdk_factory.interfaces.standardized_ssm_mixin import (
541
+ StandardizedSsmMixin,
542
542
  )
543
543
 
544
- ssm_mixin = EnhancedSsmParameterMixin()
544
+ ssm_mixin = StandardizedSsmMixin()
545
545
 
546
546
  # Setup enhanced SSM integration for auto-import
547
547
  # Use "user-pool" as resource identifier for SSM paths to match cognito exports
548
- ssm_mixin.setup_enhanced_ssm_integration(
548
+ api_gateway_config = stack_config.dictionary.get("api_gateway", {}).copy()
549
+
550
+ # Configure SSM imports for auto-discovery
551
+ if ssm_path == "auto":
552
+ if "ssm" not in api_gateway_config:
553
+ api_gateway_config["ssm"] = {}
554
+ if "imports" not in api_gateway_config["ssm"]:
555
+ api_gateway_config["ssm"]["imports"] = {}
556
+ api_gateway_config["ssm"]["imports"]["user_pool_arn"] = "/{{ORGANIZATION}}/{{ENVIRONMENT}}/cognito/user-pool/arn"
557
+
558
+ ssm_mixin.setup_standardized_ssm_integration(
549
559
  scope=self.scope,
550
- config=stack_config.dictionary.get("api_gateway", {}),
560
+ config=api_gateway_config,
551
561
  resource_type="cognito",
552
562
  resource_name="user-pool",
553
563
  )
554
564
 
555
- # If ssm_path is "auto", use auto-import mechanism
565
+ # Get user pool ARN using new pattern - read directly from config
556
566
  if ssm_path == "auto":
557
567
  logger.info("Using auto-import for user pool ARN")
558
- imported_values = ssm_mixin.auto_import_resources()
559
- user_pool_arn = imported_values.get("user_pool_arn")
568
+ ssm_imports = api_gateway_config.get("ssm", {}).get("imports", {})
569
+ user_pool_arn = ssm_imports.get("user_pool_arn")
560
570
  else:
561
571
  # Use direct parameter import for specific SSM path
562
572
  logger.info(
563
573
  f"Looking up user pool ARN from SSM parameter: {ssm_path}"
564
574
  )
565
- user_pool_arn = ssm_mixin._import_enhanced_ssm_parameter(
566
- ssm_path, "user_pool_arn"
567
- )
575
+ user_pool_arn = ssm_mixin._resolve_single_ssm_import(ssm_path, "user_pool_arn")
568
576
 
569
577
  # Extract user pool ID from ARN if we have it
570
578
  if user_pool_arn and not user_pool_id:
@@ -866,15 +874,15 @@ class ApiGatewayIntegrationUtility:
866
874
 
867
875
  if ssm_config.get("enabled", False):
868
876
  try:
869
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import (
870
- EnhancedSsmParameterMixin,
877
+ from cdk_factory.interfaces.standardized_ssm_mixin import (
878
+ StandardizedSsmMixin,
871
879
  )
872
880
 
873
- ssm_mixin = EnhancedSsmParameterMixin()
881
+ ssm_mixin = StandardizedSsmMixin()
874
882
 
875
883
  # Setup enhanced SSM integration for auto-import
876
884
  # Use consistent resource name for cross-stack compatibility
877
- ssm_mixin.setup_enhanced_ssm_integration(
885
+ ssm_mixin.setup_standardized_ssm_integration(
878
886
  scope=self.scope,
879
887
  config=api_gateway_config,
880
888
  resource_type="api-gateway",
@@ -900,7 +908,7 @@ class ApiGatewayIntegrationUtility:
900
908
  logger.info(
901
909
  f"Looking up authorizer ID from SSM parameter: {import_value}"
902
910
  )
903
- authorizer_id = ssm_mixin._import_enhanced_ssm_parameter(
911
+ authorizer_id = ssm_mixin._resolve_single_ssm_import(
904
912
  import_value, "authorizer_id"
905
913
  )
906
914
  if authorizer_id:
@@ -23,9 +23,9 @@ logger = Logger(__name__)
23
23
 
24
24
  class EnvironmentVariables:
25
25
  """
26
- Easy access to allo the environment variables we use in the appliction.
27
- It's a best practice to use this vs doing and os.getevn in each application.
28
- This helps us track all the enviroment variables in use
26
+ Easy access to allow the environment variables we use in the application.
27
+ It's a best practice to use this vs doing and os.getenv in each application.
28
+ This helps us track all the environment variables in use
29
29
  """
30
30
 
31
31
  @staticmethod
@@ -212,7 +212,7 @@ class JsonLoadingUtility:
212
212
  replacements = {
213
213
  "{{workload-name}}": "geekcafe",
214
214
  "{{deployment-name}}": "dev",
215
- "{{awsAccount}}": "123456789012",
215
+ "{{awsAccount}}": os.environ.get("AWS_ACCOUNT", "123456789012"),
216
216
  "{{hostedZoneName}}": "sandbox.geekcafe.com",
217
217
  "{{placeholder}}": "DYNAMIC_VALUE"
218
218
  }