cdk-factory 0.16.15__py3-none-any.whl → 0.18.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cdk-factory might be problematic. Click here for more details.

Files changed (59) hide show
  1. cdk_factory/configurations/base_config.py +23 -24
  2. cdk_factory/configurations/cdk_config.py +1 -1
  3. cdk_factory/configurations/deployment.py +12 -0
  4. cdk_factory/configurations/devops.py +1 -1
  5. cdk_factory/configurations/resources/acm.py +9 -2
  6. cdk_factory/configurations/resources/auto_scaling.py +2 -5
  7. cdk_factory/configurations/resources/cloudfront.py +7 -2
  8. cdk_factory/configurations/resources/ecr.py +1 -1
  9. cdk_factory/configurations/resources/ecs_cluster.py +12 -5
  10. cdk_factory/configurations/resources/ecs_service.py +7 -2
  11. cdk_factory/configurations/resources/load_balancer.py +8 -9
  12. cdk_factory/configurations/resources/monitoring.py +8 -3
  13. cdk_factory/configurations/resources/rds.py +7 -8
  14. cdk_factory/configurations/resources/rum.py +7 -2
  15. cdk_factory/configurations/resources/s3.py +1 -1
  16. cdk_factory/configurations/resources/security_group_full_stack.py +7 -8
  17. cdk_factory/configurations/resources/vpc.py +19 -0
  18. cdk_factory/configurations/workload.py +32 -2
  19. cdk_factory/constructs/ecr/ecr_construct.py +9 -2
  20. cdk_factory/constructs/lambdas/policies/policy_docs.py +4 -4
  21. cdk_factory/interfaces/istack.py +4 -4
  22. cdk_factory/interfaces/networked_stack_mixin.py +6 -6
  23. cdk_factory/interfaces/standardized_ssm_mixin.py +657 -0
  24. cdk_factory/interfaces/vpc_provider_mixin.py +64 -33
  25. cdk_factory/lambdas/edge/ip_gate/handler.py +42 -40
  26. cdk_factory/pipeline/pipeline_factory.py +3 -3
  27. cdk_factory/stack_library/__init__.py +3 -2
  28. cdk_factory/stack_library/acm/acm_stack.py +2 -2
  29. cdk_factory/stack_library/api_gateway/api_gateway_stack.py +84 -59
  30. cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +344 -535
  31. cdk_factory/stack_library/code_artifact/code_artifact_stack.py +2 -2
  32. cdk_factory/stack_library/cognito/cognito_stack.py +152 -92
  33. cdk_factory/stack_library/dynamodb/dynamodb_stack.py +19 -15
  34. cdk_factory/stack_library/ecr/ecr_stack.py +2 -2
  35. cdk_factory/stack_library/ecs/__init__.py +1 -3
  36. cdk_factory/stack_library/ecs/ecs_cluster_stack.py +157 -73
  37. cdk_factory/stack_library/ecs/ecs_service_stack.py +10 -26
  38. cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +2 -2
  39. cdk_factory/stack_library/load_balancer/load_balancer_stack.py +96 -119
  40. cdk_factory/stack_library/rds/rds_stack.py +73 -73
  41. cdk_factory/stack_library/route53/route53_stack.py +2 -2
  42. cdk_factory/stack_library/rum/rum_stack.py +108 -91
  43. cdk_factory/stack_library/security_group/security_group_full_stack.py +9 -22
  44. cdk_factory/stack_library/security_group/security_group_stack.py +11 -11
  45. cdk_factory/stack_library/stack_base.py +5 -0
  46. cdk_factory/stack_library/vpc/vpc_stack.py +272 -124
  47. cdk_factory/stack_library/websites/static_website_stack.py +1 -1
  48. cdk_factory/utilities/api_gateway_integration_utility.py +24 -16
  49. cdk_factory/utilities/environment_services.py +5 -5
  50. cdk_factory/utilities/json_loading_utility.py +1 -1
  51. cdk_factory/validation/config_validator.py +483 -0
  52. cdk_factory/version.py +1 -1
  53. {cdk_factory-0.16.15.dist-info → cdk_factory-0.18.9.dist-info}/METADATA +1 -1
  54. {cdk_factory-0.16.15.dist-info → cdk_factory-0.18.9.dist-info}/RECORD +57 -57
  55. cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -321
  56. cdk_factory/interfaces/ssm_parameter_mixin.py +0 -454
  57. {cdk_factory-0.16.15.dist-info → cdk_factory-0.18.9.dist-info}/WHEEL +0 -0
  58. {cdk_factory-0.16.15.dist-info → cdk_factory-0.18.9.dist-info}/entry_points.txt +0 -0
  59. {cdk_factory-0.16.15.dist-info → cdk_factory-0.18.9.dist-info}/licenses/LICENSE +0 -0
@@ -17,14 +17,19 @@ class BaseConfig:
17
17
  SSM parameter paths can be customized with prefixes and templates at different levels:
18
18
  1. Global level: In the workload or deployment config
19
19
  2. Stack level: In the stack config
20
- 3. Resource level: In the resource config (ssm_exports/ssm_imports)
20
+ 3. Resource level: In the resource config (ssm.exports/ssm.imports)
21
21
 
22
22
  Example configurations:
23
23
  ```json
24
24
  {
25
- "ssm_prefix_template": "/{environment}/{resource_type}/{attribute}",
26
- "ssm_exports": {
27
- "vpc_id_path": "my-vpc-id"
25
+ "ssm": {
26
+ "prefix_template": "/{environment}/{resource_type}/{attribute}",
27
+ "exports": {
28
+ "vpc_id": "my-vpc-id"
29
+ },
30
+ "imports": {
31
+ "security_group_id": "/my-app/security-group/id"
32
+ }
28
33
  }
29
34
  }
30
35
  ```
@@ -57,6 +62,16 @@ class BaseConfig:
57
62
  """
58
63
  return self.__config
59
64
 
65
+ @property
66
+ def ssm(self) -> Dict[str, Any]:
67
+ """
68
+ Get the SSM configuration for this resource.
69
+
70
+ Returns:
71
+ Dictionary containing SSM configuration with imports/exports
72
+ """
73
+ return self.__config.get("ssm", {})
74
+
60
75
  @property
61
76
  def ssm_prefix_template(self) -> str:
62
77
  """
@@ -68,7 +83,7 @@ class BaseConfig:
68
83
  Returns:
69
84
  The SSM parameter prefix template string
70
85
  """
71
- return self.__config.get("ssm_prefix_template", "/{deployment_name}/{resource_type}/{attribute}")
86
+ return self.ssm.get("prefix_template", "/{deployment_name}/{resource_type}/{attribute}")
72
87
 
73
88
  @property
74
89
  def ssm_exports(self) -> Dict[str, str]:
@@ -87,7 +102,7 @@ class BaseConfig:
87
102
  Returns:
88
103
  Dictionary mapping attribute names to SSM parameter paths for export
89
104
  """
90
- return self.__config.get("ssm_exports", {})
105
+ return self.ssm.get("exports", {})
91
106
 
92
107
  @property
93
108
  def ssm_imports(self) -> Dict[str, str]:
@@ -106,25 +121,9 @@ class BaseConfig:
106
121
  Returns:
107
122
  Dictionary mapping attribute names to SSM parameter paths for import
108
123
  """
109
- return self.__config.get("ssm_imports", {})
110
-
111
- @property
112
- def ssm_parameters(self) -> Dict[str, str]:
113
- """
114
- Get all SSM parameter path mappings (both exports and imports).
115
-
116
- This is provided for backward compatibility.
117
- New code should use ssm_exports and ssm_imports instead.
118
-
119
- Returns:
120
- Dictionary mapping attribute names to SSM parameter paths
121
- """
122
- # Merge exports and imports, with exports taking precedence
123
- combined = {**self.ssm_imports, **self.ssm_exports}
124
- # Also include any parameters directly under ssm_parameters for backward compatibility
125
- combined.update(self.__config.get("ssm_parameters", {}))
126
- return combined
124
+ return self.ssm.get("imports", {})
127
125
 
126
+
128
127
  def get(self, key: str, default: Any = None) -> Any:
129
128
  """
130
129
  Get a configuration value by key.
@@ -189,7 +189,7 @@ class CdkConfig:
189
189
  if not os.path.exists(Path(path).parent):
190
190
  os.makedirs(Path(path).parent)
191
191
  cdk = config.get("cdk", {})
192
- if replacements and len(replacements) > 0:
192
+ if replacements:
193
193
  config = JsonLoadingUtility.recursive_replace(config, replacements)
194
194
  print(f"📀 Saving config to {path}")
195
195
  # add the original cdk back
@@ -28,6 +28,18 @@ class DeploymentConfig:
28
28
  self.__load()
29
29
 
30
30
  def __load(self):
31
+ # Validate environment consistency
32
+ deployment_env = self.__deployment.get("environment")
33
+ workload_env = self.__workload.get("environment")
34
+
35
+ if deployment_env and workload_env and deployment_env != workload_env:
36
+ from aws_lambda_powertools import Logger
37
+ logger = Logger()
38
+ logger.warning(
39
+ f"Environment mismatch: deployment.environment='{deployment_env}' != workload.environment='{workload_env}'. "
40
+ f"Using workload.environment for consistency."
41
+ )
42
+
31
43
  self.__load_pipeline()
32
44
  self.__load_stacks()
33
45
 
@@ -64,7 +64,7 @@ class DevOps:
64
64
  )
65
65
  if (
66
66
  not self.__code_repository.repository
67
- or len(self.__code_repository.repository) == 0
67
+ or not self.__code_repository.repository
68
68
  ):
69
69
  raise ValueError(
70
70
  "Code Repository is not defined in the configuration "
@@ -59,15 +59,22 @@ class AcmConfig:
59
59
  """Certificate transparency logging preference (ENABLED or DISABLED)"""
60
60
  return self.__config.get("certificate_transparency_logging_preference")
61
61
 
62
+ @property
63
+ def ssm(self) -> Dict[str, Any]:
64
+ """SSM configuration for importing/exporting resources"""
65
+ return self.__config.get("ssm", {})
66
+
62
67
  @property
63
68
  def ssm_exports(self) -> Dict[str, str]:
64
69
  """SSM parameter paths to export certificate details"""
65
- exports = self.__config.get("ssm_exports", {})
70
+ exports = self.ssm.get("exports", {})
66
71
 
67
72
  # Provide default SSM export path if not specified
68
73
  if not exports and self.__deployment:
74
+ workload_env = self.__deployment.workload.get("environment", self.__deployment.environment)
75
+ workload_name = self.__deployment.workload.get("name", self.__deployment.workload_name)
69
76
  exports = {
70
- "certificate_arn": f"/{self.__deployment.environment}/{self.__deployment.workload_name}/certificate/arn"
77
+ "certificate_arn": f"/{workload_env}/{workload_name}/certificate/arn"
71
78
  }
72
79
 
73
80
  return exports
@@ -70,12 +70,9 @@ class AutoScalingConfig(EnhancedBaseConfig):
70
70
  return self.__config.get("termination_policies", ["DEFAULT"])
71
71
 
72
72
  @property
73
- def update_policy(self) -> Dict[str, Any]:
73
+ def update_policy(self) -> Optional[Dict[str, Any]]:
74
74
  """Update policy configuration"""
75
- return self.__config.get(
76
- "update_policy",
77
- {"min_instances_in_service": 1, "max_batch_size": 1, "pause_time": 300},
78
- )
75
+ return self.__config.get("update_policy")
79
76
 
80
77
  @property
81
78
  def user_data_commands(self) -> List[str]:
@@ -102,15 +102,20 @@ class CloudFrontConfig(EnhancedBaseConfig):
102
102
  """Resource tags"""
103
103
  return self._config.get("tags", {})
104
104
 
105
+ @property
106
+ def ssm(self) -> Dict[str, Any]:
107
+ """SSM configuration"""
108
+ return self._config.get("ssm", {})
109
+
105
110
  @property
106
111
  def ssm_exports(self) -> Dict[str, str]:
107
112
  """SSM parameter exports"""
108
- return self._config.get("ssm_exports", {})
113
+ return self.ssm.get("exports", {})
109
114
 
110
115
  @property
111
116
  def ssm_imports(self) -> Dict[str, str]:
112
117
  """SSM parameter imports"""
113
- return self._config.get("ssm_imports", {})
118
+ return self.ssm.get("imports", {})
114
119
 
115
120
  @property
116
121
  def hosted_zone_id(self) -> str:
@@ -128,7 +128,7 @@ class ECRConfig(EnhancedBaseConfig):
128
128
  Example:
129
129
  {
130
130
  "enabled": true,
131
- "accounts": ["123456789012", "987654321098"],
131
+ "accounts": [os.environ.get("ECR_ALLOWED_ACCOUNT_1"), os.environ.get("ECR_ALLOWED_ACCOUNT_2")],
132
132
  "services": [
133
133
  {
134
134
  "name": "lambda",
@@ -18,6 +18,11 @@ class EcsClusterConfig:
18
18
  def __init__(self, config: Dict[str, Any]) -> None:
19
19
  self._config = config or {}
20
20
 
21
+ @property
22
+ def dictionary(self) -> Dict[str, Any]:
23
+ """Access to the underlying configuration dictionary (for compatibility with SSM mixin)"""
24
+ return self._config
25
+
21
26
  @property
22
27
  def name(self) -> str:
23
28
  """Name of the ECS cluster. Supports template variables like {{WORKLOAD_NAME}}-{{ENVIRONMENT}}-cluster"""
@@ -87,15 +92,17 @@ class EcsClusterConfig:
87
92
  return self._config.get("export_ssm_parameters", True)
88
93
 
89
94
 
95
+ @property
96
+ def ssm(self) -> Dict[str, Any]:
97
+ """SSM configuration"""
98
+ return self._config.get("ssm", {})
99
+
90
100
  @property
91
101
  def ssm_exports(self) -> Dict[str, str]:
92
102
  """SSM parameter exports"""
93
- return self._config.get("ssm_exports", {})
103
+ return self.ssm.get("exports", {})
94
104
 
95
105
  @property
96
106
  def ssm_imports(self) -> Dict[str, Any]:
97
107
  """SSM parameter imports"""
98
- # Check both nested and flat structures for backwards compatibility
99
- if "ssm" in self._config and "imports" in self._config["ssm"]:
100
- return self._config["ssm"]["imports"]
101
- return self._config.get("ssm_imports", {})
108
+ return self.ssm.get("imports", {})
@@ -123,10 +123,15 @@ class EcsServiceConfig:
123
123
  """Resource tags"""
124
124
  return self._config.get("tags", {})
125
125
 
126
+ @property
127
+ def ssm(self) -> Dict[str, Any]:
128
+ """SSM configuration"""
129
+ return self._config.get("ssm", {})
130
+
126
131
  @property
127
132
  def ssm_exports(self) -> Dict[str, str]:
128
133
  """SSM parameter exports"""
129
- return self._config.get("ssm_exports", {})
134
+ return self.ssm.get("exports", {})
130
135
 
131
136
  @property
132
137
  def ssm_imports(self) -> Dict[str, Any]:
@@ -134,7 +139,7 @@ class EcsServiceConfig:
134
139
  # Check both nested and flat structures for backwards compatibility
135
140
  if "ssm" in self._config and "imports" in self._config["ssm"]:
136
141
  return self._config["ssm"]["imports"]
137
- return self._config.get("ssm_imports", {})
142
+ return self.ssm.get("imports", {})
138
143
 
139
144
  @property
140
145
  def deployment_type(self) -> str:
@@ -144,21 +144,20 @@ class LoadBalancerConfig(EnhancedBaseConfig):
144
144
  "block_response", default_response
145
145
  )
146
146
 
147
+ @property
148
+ def ssm(self) -> Dict[str, Any]:
149
+ """SSM configuration"""
150
+ return self.__config.get("ssm", {})
151
+
147
152
  @property
148
153
  def ssm_imports(self) -> Dict[str, Any]:
149
154
  """SSM parameter imports for the Load Balancer"""
150
- # Check both nested and flat structures for backwards compatibility
151
- if "ssm" in self.__config and "imports" in self.__config["ssm"]:
152
- return self.__config["ssm"]["imports"]
153
- return self.__config.get("ssm_imports", {})
154
-
155
+ return self.ssm.get("imports", {})
156
+
155
157
  @property
156
158
  def ssm_exports(self) -> Dict[str, Any]:
157
159
  """SSM parameter exports for the Load Balancer"""
158
- # Check both nested and flat structures for backwards compatibility
159
- if "ssm" in self.__config and "exports" in self.__config["ssm"]:
160
- return self.__config["ssm"]["exports"]
161
- return self.__config.get("ssm_exports", {})
160
+ return self.ssm.get("exports", {})
162
161
 
163
162
  @property
164
163
  def ssm_parameters(self) -> Dict[str, Any]:
@@ -18,7 +18,7 @@ class MonitoringConfig(EnhancedBaseConfig):
18
18
  super().__init__(
19
19
  config or {},
20
20
  resource_type="monitoring",
21
- resource_name=config.get("name", "monitoring") if config else "monitoring"
21
+ resource_name=config.get("name", "monitoring") if config else "monitoring",
22
22
  )
23
23
  self._config = config or {}
24
24
  self._deployment = deployment
@@ -63,12 +63,17 @@ class MonitoringConfig(EnhancedBaseConfig):
63
63
  """Resource tags"""
64
64
  return self._config.get("tags", {})
65
65
 
66
+ @property
67
+ def ssm(self) -> Dict[str, Any]:
68
+ """SSM configuration"""
69
+ return self._config.get("ssm", {})
70
+
66
71
  @property
67
72
  def ssm_exports(self) -> Dict[str, str]:
68
73
  """SSM parameter exports"""
69
- return self._config.get("ssm_exports", {})
74
+ return self.ssm.get("exports", {})
70
75
 
71
76
  @property
72
77
  def ssm_imports(self) -> Dict[str, str]:
73
78
  """SSM parameter imports for resource ARNs"""
74
- return self._config.get("ssm_imports", {})
79
+ return self.ssm.get("imports", {})
@@ -261,21 +261,20 @@ class RdsConfig(EnhancedBaseConfig):
261
261
  """Sets the VPC ID for the Security Group"""
262
262
  self.__config["vpc_id"] = value
263
263
 
264
+ @property
265
+ def ssm(self) -> Dict[str, Any]:
266
+ """SSM configuration"""
267
+ return self.__config.get("ssm", {})
268
+
264
269
  @property
265
270
  def ssm_imports(self) -> Dict[str, str]:
266
271
  """SSM parameter imports for the RDS instance"""
267
- # Check both nested and flat structures for backwards compatibility
268
- if "ssm" in self.__config and "imports" in self.__config["ssm"]:
269
- return self.__config["ssm"]["imports"]
270
- return self.__config.get("ssm_imports", {})
272
+ return self.ssm.get("imports", {})
271
273
 
272
274
  @property
273
275
  def ssm_exports(self) -> Dict[str, str]:
274
276
  """SSM parameter exports for the RDS instance"""
275
- # Check both nested and flat structures for backwards compatibility
276
- if "ssm" in self.__config and "exports" in self.__config["ssm"]:
277
- return self.__config["ssm"]["exports"]
278
- return self.__config.get("ssm_exports", {})
277
+ return self.ssm.get("exports", {})
279
278
 
280
279
  def _sanitize_database_name(self, name: str) -> str:
281
280
  """
@@ -125,11 +125,16 @@ class RumConfig(EnhancedBaseConfig):
125
125
 
126
126
  # SSM Integration
127
127
  @property
128
+ def ssm(self) -> Dict[str, Any]:
129
+ """SSM configuration for importing/exporting resources"""
130
+ return self.__config.get("ssm", {})
131
+
132
+ @property
128
133
  def ssm_exports(self) -> Dict[str, str]:
129
134
  """SSM parameter paths for exporting RUM resources"""
130
- return self.__config.get("ssm_exports", {})
135
+ return self.ssm.get("exports", {})
131
136
 
132
137
  @property
133
138
  def ssm_imports(self) -> Dict[str, str]:
134
139
  """SSM parameter paths for importing external resources"""
135
- return self.__config.get("ssm_imports", {})
140
+ return self.ssm.get("imports", {})
@@ -30,7 +30,7 @@ class S3BucketConfig(EnhancedBaseConfig):
30
30
  "S3 Bucket Configuration must be a dictionary. Found: "
31
31
  f"{type(self.__config)}"
32
32
  )
33
- if len(self.__config.keys()) == 0:
33
+ if not self.__config.keys():
34
34
  raise ValueError("S3 Bucket Configuration cannot be empty")
35
35
 
36
36
  @property
@@ -62,21 +62,20 @@ class SecurityGroupFullStackConfig:
62
62
  """Tags to apply to the Security Group"""
63
63
  return self.__config.get("tags", {})
64
64
 
65
+ @property
66
+ def ssm(self) -> Dict[str, Any]:
67
+ """SSM configuration"""
68
+ return self.__config.get("ssm", {})
69
+
65
70
  @property
66
71
  def ssm_imports(self) -> Dict[str, str]:
67
72
  """SSM parameter imports for the Security Group"""
68
- # Check both nested and flat structures for backwards compatibility
69
- if "ssm" in self.__config and "imports" in self.__config["ssm"]:
70
- return self.__config["ssm"]["imports"]
71
- return self.__config.get("ssm_imports", {})
73
+ return self.ssm.get("imports", {})
72
74
 
73
75
  @property
74
76
  def ssm_exports(self) -> Dict[str, str]:
75
77
  """SSM parameter exports for the Security Group"""
76
- # Check both nested and flat structures for backwards compatibility
77
- if "ssm" in self.__config and "exports" in self.__config["ssm"]:
78
- return self.__config["ssm"]["exports"]
79
- return self.__config.get("ssm_exports", {})
78
+ return self.ssm.get("exports", {})
80
79
 
81
80
  @property
82
81
  def security_groups(self) -> List[Dict[str, Any]]:
@@ -122,3 +122,22 @@ class VpcConfig(EnhancedBaseConfig):
122
122
  def isolated_subnet_name(self) -> str:
123
123
  """Custom name for isolated subnets"""
124
124
  return self.get("isolated_subnet_name", "isolated")
125
+
126
+ @property
127
+ def subnets(self) -> Dict[str, Any]:
128
+ """Subnet configuration for the VPC"""
129
+ return self.get("subnets", {
130
+ "public": {
131
+ "enabled": self.public_subnets,
132
+ "cidr_mask": self.public_subnet_mask,
133
+ "map_public_ip": True
134
+ },
135
+ "private": {
136
+ "enabled": self.private_subnets,
137
+ "cidr_mask": self.private_subnet_mask
138
+ },
139
+ "isolated": {
140
+ "enabled": self.isolated_subnets,
141
+ "cidr_mask": self.isolated_subnet_mask
142
+ }
143
+ })
@@ -5,6 +5,7 @@ MIT License. See Project Root for the license information.
5
5
  """
6
6
 
7
7
  import os
8
+ import copy
8
9
  from typing import Any, Dict, List
9
10
 
10
11
  from aws_lambda_powertools import Logger
@@ -66,13 +67,42 @@ class WorkloadConfig:
66
67
  if self.__app_config is None:
67
68
  raise ValueError("Configuration is not defined.")
68
69
 
70
+ # Create a deep copy to avoid mutating the original configuration
69
71
  if "workload" in self.__app_config:
70
- workload = self.__app_config["workload"]
72
+ workload = copy.deepcopy(self.__app_config["workload"])
71
73
  else:
72
- workload = self.__app_config
74
+ workload = copy.deepcopy(self.__app_config)
73
75
 
74
76
  self.__workload = workload
75
77
 
78
+ # Handle missing devops section gracefully
79
+ if "devops" not in workload:
80
+ logger.warning("Devops configuration not found in workload, using defaults")
81
+
82
+ # Get environment variables for defaults
83
+ devops_account = os.environ.get("DEVOPS_AWS_ACCOUNT")
84
+ devops_region = os.environ.get("DEVOPS_REGION")
85
+
86
+ # Validate required environment variables
87
+ if not devops_account or not devops_region:
88
+ raise ValueError(
89
+ "DEVOPS_AWS_ACCOUNT and DEVOPS_REGION environment variables must be set when devops config is missing"
90
+ )
91
+
92
+ # Use a separate defaults object instead of mutating the original
93
+ devops_defaults = {
94
+ "account": devops_account,
95
+ "region": devops_region,
96
+ "code_repository": {
97
+ "name": os.environ.get("CODE_REPOSITORY_NAME", "default-repo"),
98
+ "type": "connector_arn",
99
+ "connector_arn": os.environ.get("CODE_REPOSITORY_ARN", f"arn:aws:codeconnections:{os.environ.get('DEVOPS_REGION', 'us-east-1')}:{os.environ.get('DEVOPS_AWS_ACCOUNT')}:connection/default")
100
+ },
101
+ "commands": []
102
+ }
103
+
104
+ workload["devops"] = devops_defaults
105
+
76
106
  self.__devops = DevOps(workload["devops"])
77
107
  self.__management = Management(workload.get("management", {}))
78
108
  self.__cloudfront = CloudFrontConfig(workload.get("cloudfront", {}))
@@ -14,12 +14,12 @@ from constructs import Construct, IConstruct
14
14
  from cdk_factory.configurations.resources.resource_types import ResourceTypes
15
15
  from cdk_factory.configurations.resources.ecr import ECRConfig as ECR
16
16
  from cdk_factory.configurations.deployment import DeploymentConfig as Deployment
17
- from cdk_factory.interfaces.ssm_parameter_mixin import SsmParameterMixin
17
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
18
18
 
19
19
  logger = Logger(__name__)
20
20
 
21
21
 
22
- class ECRConstruct(Construct, SsmParameterMixin):
22
+ class ECRConstruct(Construct, StandardizedSsmMixin):
23
23
  def __init__(
24
24
  self,
25
25
  scope: Construct,
@@ -30,6 +30,8 @@ class ECRConstruct(Construct, SsmParameterMixin):
30
30
  **kwargs,
31
31
  ) -> None:
32
32
  super().__init__(scope, id, **kwargs)
33
+ # Initialize StandardizedSsmMixin explicitly
34
+ StandardizedSsmMixin.__init__(self, **kwargs)
33
35
 
34
36
  self.scope = scope
35
37
  self.deployment = deployment
@@ -77,6 +79,11 @@ class ECRConstruct(Construct, SsmParameterMixin):
77
79
 
78
80
  This method uses the new configurable SSM parameter prefix system.
79
81
  """
82
+ # Check if SSM exports are configured
83
+ if not hasattr(self.repo, 'ssm_exports') or not self.repo.ssm_exports:
84
+ logger.debug("No SSM exports configured for ECR repository")
85
+ return
86
+
80
87
  # Create a dictionary of resource values to export
81
88
  resource_values = {
82
89
  "name": self.ecr.repository_name,
@@ -35,18 +35,18 @@ class ResourceResolver:
35
35
  """Get or create enhanced SSM parameter mixin"""
36
36
  if self._ssm_mixin is None:
37
37
  try:
38
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import (
39
- EnhancedSsmParameterMixin,
38
+ from cdk_factory.interfaces.standardized_ssm_mixin import (
39
+ StandardizedSsmMixin,
40
40
  )
41
41
 
42
- self._ssm_mixin = EnhancedSsmParameterMixin()
42
+ self._ssm_mixin = StandardizedSsmMixin()
43
43
 
44
44
  # Setup enhanced SSM integration if lambda config has SSM settings
45
45
  lambda_dict = getattr(self.lambda_config, "dictionary", {})
46
46
  ssm_config = lambda_dict.get("ssm", {})
47
47
 
48
48
  if ssm_config.get("enabled", False):
49
- self._ssm_mixin.setup_enhanced_ssm_integration(
49
+ self._ssm_mixin.setup_ssm_integration(
50
50
  scope=self.scope,
51
51
  config=lambda_dict,
52
52
  resource_type="lambda",
@@ -8,14 +8,14 @@ from abc import ABCMeta, abstractmethod
8
8
  import jsii
9
9
  from constructs import Construct
10
10
  from aws_cdk import Stack
11
- from cdk_factory.interfaces.ssm_parameter_mixin import SsmParameterMixin
11
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
12
12
 
13
13
 
14
14
  class StackABCMeta(jsii.JSIIMeta, ABCMeta):
15
15
  """StackABCMeta"""
16
16
 
17
17
 
18
- class IStack(Stack, SsmParameterMixin, metaclass=StackABCMeta):
18
+ class IStack(Stack, StandardizedSsmMixin, metaclass=StackABCMeta):
19
19
  """
20
20
  IStack for Dynamically loaded Factory Stacks
21
21
  Only imports from constructs and abc to avoid circular dependencies.
@@ -25,8 +25,8 @@ class IStack(Stack, SsmParameterMixin, metaclass=StackABCMeta):
25
25
  def __init__(self, scope: Construct, id: str, **kwargs) -> None:
26
26
  # Initialize Stack first
27
27
  Stack.__init__(self, scope, id, **kwargs)
28
- # Initialize SsmParameterMixin (no super() call to avoid MRO issues)
29
- SsmParameterMixin.__init__(self, **kwargs)
28
+ # Initialize StandardizedSsmMixin (no super() call to avoid MRO issues)
29
+ StandardizedSsmMixin.__init__(self, **kwargs)
30
30
 
31
31
  @abstractmethod
32
32
  def build(self, *, stack_config, deployment, workload) -> None:
@@ -6,16 +6,16 @@ MIT License. See Project Root for license information.
6
6
 
7
7
  from typing import Any
8
8
  from aws_cdk import aws_ec2 as ec2
9
- from .ssm_parameter_mixin import SsmParameterMixin
9
+ from .standardized_ssm_mixin import StandardizedSsmMixin
10
10
  from .vpc_provider_mixin import VPCProviderMixin
11
11
 
12
12
 
13
- class NetworkedStackMixin(SsmParameterMixin, VPCProviderMixin):
13
+ class NetworkedStackMixin(StandardizedSsmMixin, VPCProviderMixin):
14
14
  """
15
15
  Combined mixin for stacks that need both SSM imports and VPC resolution.
16
16
 
17
17
  This mixin provides a complete solution for network-aware stacks by combining:
18
- - Enhanced SSM parameter import functionality (with caching and list support)
18
+ - Enhanced SSM parameter import functionality (with standardized configuration)
19
19
  - VPC resolution with multiple fallback strategies
20
20
  - Standardized initialization patterns
21
21
 
@@ -23,17 +23,17 @@ class NetworkedStackMixin(SsmParameterMixin, VPCProviderMixin):
23
23
  class MyStack(Stack, NetworkedStackMixin):
24
24
  def __init__(self, scope, id, **kwargs):
25
25
  super().__init__(scope, id, **kwargs)
26
- # SSM initialization is handled automatically by SsmParameterMixin.__init__
26
+ # SSM initialization is handled automatically by StandardizedSsmMixin.__init__
27
27
 
28
28
  def _build(self, stack_config, deployment, workload):
29
- self.process_ssm_imports(stack_config, deployment, "my-resource")
29
+ self.setup_ssm_integration(scope=self, config=stack_config.dictionary, resource_type="my-resource", resource_name="my-name")
30
30
  self.vpc = self.resolve_vpc(stack_config, deployment, workload)
31
31
  """
32
32
 
33
33
  def _initialize_networked_stack(self) -> None:
34
34
  """
35
35
  Initialize all networked stack functionality.
36
- Note: SSM initialization is handled by SsmParameterMixin.__init__
36
+ Note: SSM initialization is handled by StandardizedSsmMixin.__init__
37
37
  """
38
38
  self._initialize_vpc_cache()
39
39