cdk-factory 0.16.15__py3-none-any.whl → 0.20.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.

Potentially problematic release.


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

Files changed (66) 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 +7 -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 +30 -3
  11. cdk_factory/configurations/resources/lambda_edge.py +18 -4
  12. cdk_factory/configurations/resources/load_balancer.py +8 -9
  13. cdk_factory/configurations/resources/monitoring.py +8 -3
  14. cdk_factory/configurations/resources/rds.py +8 -9
  15. cdk_factory/configurations/resources/route53.py +5 -0
  16. cdk_factory/configurations/resources/rum.py +7 -2
  17. cdk_factory/configurations/resources/s3.py +10 -2
  18. cdk_factory/configurations/resources/security_group_full_stack.py +7 -8
  19. cdk_factory/configurations/resources/vpc.py +19 -0
  20. cdk_factory/configurations/workload.py +32 -2
  21. cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py +1 -1
  22. cdk_factory/constructs/ecr/ecr_construct.py +9 -2
  23. cdk_factory/constructs/lambdas/policies/policy_docs.py +4 -4
  24. cdk_factory/interfaces/istack.py +4 -4
  25. cdk_factory/interfaces/networked_stack_mixin.py +6 -6
  26. cdk_factory/interfaces/standardized_ssm_mixin.py +684 -0
  27. cdk_factory/interfaces/vpc_provider_mixin.py +64 -33
  28. cdk_factory/lambdas/edge/ip_gate/handler.py +42 -40
  29. cdk_factory/pipeline/pipeline_factory.py +3 -3
  30. cdk_factory/stack_library/__init__.py +3 -2
  31. cdk_factory/stack_library/acm/acm_stack.py +7 -17
  32. cdk_factory/stack_library/api_gateway/api_gateway_stack.py +84 -59
  33. cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +454 -537
  34. cdk_factory/stack_library/cloudfront/cloudfront_stack.py +76 -22
  35. cdk_factory/stack_library/code_artifact/code_artifact_stack.py +5 -27
  36. cdk_factory/stack_library/cognito/cognito_stack.py +152 -92
  37. cdk_factory/stack_library/dynamodb/dynamodb_stack.py +19 -15
  38. cdk_factory/stack_library/ecr/ecr_stack.py +2 -2
  39. cdk_factory/stack_library/ecs/__init__.py +1 -3
  40. cdk_factory/stack_library/ecs/ecs_cluster_stack.py +159 -75
  41. cdk_factory/stack_library/ecs/ecs_service_stack.py +59 -52
  42. cdk_factory/stack_library/lambda_edge/EDGE_LOG_RETENTION_TODO.md +226 -0
  43. cdk_factory/stack_library/lambda_edge/LAMBDA_EDGE_LOG_RETENTION_BLOG.md +215 -0
  44. cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +240 -83
  45. cdk_factory/stack_library/load_balancer/load_balancer_stack.py +139 -212
  46. cdk_factory/stack_library/rds/rds_stack.py +74 -98
  47. cdk_factory/stack_library/route53/route53_stack.py +246 -40
  48. cdk_factory/stack_library/rum/rum_stack.py +108 -91
  49. cdk_factory/stack_library/security_group/security_group_full_stack.py +10 -53
  50. cdk_factory/stack_library/security_group/security_group_stack.py +12 -19
  51. cdk_factory/stack_library/simple_queue_service/sqs_stack.py +1 -34
  52. cdk_factory/stack_library/stack_base.py +5 -0
  53. cdk_factory/stack_library/vpc/vpc_stack.py +171 -130
  54. cdk_factory/stack_library/websites/static_website_stack.py +7 -3
  55. cdk_factory/utilities/api_gateway_integration_utility.py +24 -16
  56. cdk_factory/utilities/environment_services.py +5 -5
  57. cdk_factory/utilities/json_loading_utility.py +1 -1
  58. cdk_factory/validation/config_validator.py +483 -0
  59. cdk_factory/version.py +1 -1
  60. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/METADATA +1 -1
  61. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/RECORD +64 -62
  62. cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -321
  63. cdk_factory/interfaces/ssm_parameter_mixin.py +0 -454
  64. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/WHEEL +0 -0
  65. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/entry_points.txt +0 -0
  66. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,454 +0,0 @@
1
- """
2
- Geek Cafe, LLC
3
- Maintainers: Eric Wilson
4
- MIT License. See Project Root for the license information.
5
- """
6
-
7
- from typing import Dict, Any, Optional, Union, List
8
- from aws_cdk import aws_ssm as ssm
9
- from constructs import Construct
10
- from aws_lambda_powertools import Logger
11
-
12
- logger = Logger(__name__)
13
-
14
-
15
- class SsmParameterMixin:
16
- """
17
- A mixin class that provides SSM parameter export and import functionality
18
- for CDK stacks.
19
-
20
- This mixin should be used by stack classes to standardize how SSM parameters
21
- are exported and imported across the project.
22
-
23
- Enhanced to support:
24
- - List parameter imports (for security groups, etc.)
25
- - Cached imported values for easy access
26
- - Backward compatibility with existing interfaces
27
- """
28
-
29
- def __init__(self, *args, **kwargs):
30
- """Initialize the mixin with cached storage for imported values."""
31
- # Don't call super() to avoid MRO issues in multiple inheritance
32
- # Initialize cached storage for imported values
33
- self._ssm_imported_values: Dict[str, Union[str, List[str]]] = {}
34
-
35
- def initialize_ssm_imports(self) -> None:
36
- """
37
- Initialize SSM imports storage.
38
- Call this in your stack's __init__ method if not using __init__ above.
39
- """
40
- if not hasattr(self, '_ssm_imported_values'):
41
- self._ssm_imported_values: Dict[str, Union[str, List[str]]] = {}
42
-
43
- def get_ssm_imported_value(self, key: str, default: Any = None) -> Any:
44
- """
45
- Get a cached SSM imported value by key.
46
-
47
- Args:
48
- key: The SSM import key
49
- default: Default value if key not found
50
-
51
- Returns:
52
- The imported value or default
53
- """
54
- return self._ssm_imported_values.get(key, default)
55
-
56
- def has_ssm_import(self, key: str) -> bool:
57
- """
58
- Check if an SSM import key exists in cached values.
59
-
60
- Args:
61
- key: The SSM import key to check
62
-
63
- Returns:
64
- True if key exists, False otherwise
65
- """
66
- return key in self._ssm_imported_values
67
-
68
- def process_ssm_imports(
69
- self,
70
- config: Any,
71
- deployment: Any,
72
- resource_type: str = "resource"
73
- ) -> None:
74
- """
75
- Process SSM imports from configuration and cache them for later use.
76
-
77
- This method handles list imports (like security_group_ids) and caches
78
- the results for easy access via get_ssm_imported_value().
79
-
80
- Args:
81
- config: The configuration object with ssm_imports property
82
- deployment: The deployment configuration for path resolution
83
- resource_type: Type of resource for logging purposes
84
- """
85
- if not hasattr(config, 'ssm_imports'):
86
- logger.debug(f"No ssm_imports property found on config for {resource_type}")
87
- return
88
-
89
- ssm_imports = config.ssm_imports
90
-
91
- if not ssm_imports:
92
- logger.debug(f"No SSM imports configured for {resource_type}")
93
- return
94
-
95
- logger.info(f"Processing {len(ssm_imports)} SSM imports for {resource_type}")
96
-
97
- for param_key, param_value in ssm_imports.items():
98
- try:
99
- if isinstance(param_value, list):
100
- # Handle list imports (like security_group_ids)
101
- imported_list = []
102
- for item in param_value:
103
- param_path = self._resolve_ssm_path(item, deployment)
104
-
105
- construct_id = f"ssm-import-{param_key}-{hash(param_path) % 10000}"
106
- param = ssm.StringParameter.from_string_parameter_name(
107
- self, construct_id, param_path
108
- )
109
- imported_list.append(param.string_value)
110
-
111
- self._ssm_imported_values[param_key] = imported_list
112
- logger.info(f"Imported SSM parameter list: {param_key} with {len(imported_list)} items")
113
- else:
114
- # Handle string values
115
- param_path = self._resolve_ssm_path(param_value, deployment)
116
-
117
- construct_id = f"ssm-import-{param_key}-{hash(param_path) % 10000}"
118
- param = ssm.StringParameter.from_string_parameter_name(
119
- self, construct_id, param_path
120
- )
121
-
122
- self._ssm_imported_values[param_key] = param.string_value
123
- logger.info(f"Imported SSM parameter: {param_key} from {param_path}")
124
-
125
- except Exception as e:
126
- logger.error(f"Failed to import SSM parameter {param_key}: {e}")
127
- raise
128
-
129
- def _resolve_ssm_path(self, path: str, deployment: Any) -> str:
130
- """
131
- Resolve SSM parameter path (handle relative vs absolute paths).
132
-
133
- Args:
134
- path: The parameter path from configuration
135
- deployment: The deployment configuration for context
136
-
137
- Returns:
138
- Fully resolved SSM parameter path
139
- """
140
- if not path.startswith('/'):
141
- # Convert relative path to absolute path
142
- return f"/{deployment.environment}/{deployment.workload_name}/{path}"
143
- return path
144
-
145
- @staticmethod
146
- def normalize_resource_name(name: str, for_export: bool = False) -> str:
147
- """
148
- Normalize resource names for consistent naming across CDK stacks.
149
-
150
- Args:
151
- name: The resource name to normalize
152
- for_export: If True, keeps hyphens for CloudFormation export compatibility
153
-
154
- Returns:
155
- Normalized name with consistent convention
156
-
157
- Examples:
158
- "web-servers" -> "web_servers" (for SSM parameters)
159
- "web-servers" -> "web-servers" (for CloudFormation exports, for_export=True)
160
- "API-Gateway" -> "api_gateway" or "api-gateway"
161
- """
162
- if for_export:
163
- # CloudFormation exports only allow alphanumeric, colons, hyphens
164
- return name.lower()
165
- else:
166
- # SSM parameters use underscores for consistency
167
- return name.replace("-", "_").lower()
168
-
169
- def export_ssm_parameter(
170
- self,
171
- scope: Construct,
172
- id: str,
173
- value: str,
174
- parameter_name: str,
175
- description: str = None,
176
- string_list_value: bool = False,
177
- ) -> ssm.StringParameter:
178
- """
179
- Export a value to SSM Parameter Store.
180
-
181
- Args:
182
- scope: The CDK construct scope
183
- id: The construct ID for the SSM parameter
184
- value: The value to store in the parameter
185
- parameter_name: The name of the parameter in SSM
186
- description: Optional description for the parameter
187
- string_list_value: Whether the parameter is a comma-delimited list
188
-
189
- Returns:
190
- The created SSM parameter
191
- """
192
- if not parameter_name:
193
- logger.warning(f"No SSM parameter name provided for {id}, skipping export")
194
- return None
195
-
196
- # Ensure parameter name starts with a slash
197
- if not parameter_name.startswith("/"):
198
- parameter_name = f"/{parameter_name}"
199
-
200
- logger.info(f"Exporting SSM parameter: {parameter_name}")
201
-
202
- return ssm.StringParameter(
203
- scope=scope,
204
- id=id,
205
- string_value=value,
206
- parameter_name=parameter_name,
207
- description=description,
208
- )
209
-
210
- def import_ssm_parameter(
211
- self,
212
- scope: Construct,
213
- id: str,
214
- parameter_name: str,
215
- version: Optional[int] = None,
216
- ) -> str:
217
- """
218
- Import a value from SSM Parameter Store.
219
-
220
- Args:
221
- scope: The CDK construct scope
222
- id: The construct ID for the SSM parameter reference
223
- parameter_name: The name of the parameter in SSM
224
- version: Optional specific version to retrieve
225
-
226
- Returns:
227
- The parameter value as a string
228
- """
229
- if not parameter_name:
230
- logger.warning(f"No SSM parameter name provided for {id}, cannot import")
231
- return None
232
-
233
- # Ensure parameter name starts with a slash
234
- if not parameter_name.startswith("/"):
235
- parameter_name = f"/{parameter_name}"
236
-
237
- logger.info(f"Importing SSM parameter: {parameter_name}")
238
-
239
- if version:
240
- return ssm.StringParameter.from_string_parameter_attributes(
241
- scope,
242
- id,
243
- parameter_name=parameter_name,
244
- version=version,
245
- ).string_value
246
- else:
247
- return ssm.StringParameter.from_string_parameter_name(
248
- scope,
249
- id,
250
- parameter_name,
251
- ).string_value
252
-
253
- def export_ssm_parameters_from_config(
254
- self,
255
- scope: Construct,
256
- config_dict: Dict[str, Any],
257
- ssm_config: Dict[str, str],
258
- resource: str = "",
259
- ) -> Dict[str, ssm.StringParameter]:
260
- """
261
- Export multiple SSM parameters based on a configuration dictionary and SSM path mapping.
262
-
263
- Args:
264
- scope: The CDK construct scope
265
- config_dict: Dictionary containing values to export
266
- ssm_config: Dictionary mapping keys to SSM parameter paths
267
- resource: Optional resource name for the parameter IDs
268
-
269
- Returns:
270
- Dictionary of created SSM parameters
271
- """
272
- parameters = {}
273
- missing_keys = []
274
- for key, path in ssm_config.items():
275
- if key not in config_dict:
276
- # missing or misspelled key
277
- missing_keys.append(key)
278
- continue
279
-
280
- if not path:
281
- # nothing configured for this key which is acceptable
282
- continue
283
-
284
- value = str(config_dict[key])
285
- id_name = f"{resource}{key.replace('_', '-')}-param"
286
-
287
- param = self.export_ssm_parameter(
288
- scope=scope,
289
- id=id_name,
290
- value=value,
291
- parameter_name=path,
292
- description=f"Exported {key} from {resource}",
293
- )
294
-
295
- if param:
296
- parameters[key] = param
297
-
298
- if missing_keys:
299
- logger.warning(f"Missing keys: {missing_keys}")
300
- # TODO : throw an exception here?
301
- message = (
302
- "🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨"
303
- f"\nThe following keys are missing from the config dictionary: {missing_keys}."
304
- f"\nThe accepted keys are: {list(config_dict.keys())}."
305
- "\nPlease check your configuration. Some keys may be misspelled."
306
- "\n🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨🚨"
307
- )
308
- print(message)
309
- logger.error(message.replace("\n", ""))
310
- exit(1)
311
-
312
- return parameters
313
-
314
- def export_resource_to_ssm(
315
- self,
316
- scope: Construct,
317
- resource_values: Dict[str, Any],
318
- config: Any, # Should be a BaseConfig subclass
319
- resource_name: str,
320
- resource_type: str = None,
321
- context: Dict[str, Any] = None,
322
- ) -> Dict[str, ssm.StringParameter]:
323
- """
324
- Export resource attributes to SSM Parameter Store based on configuration.
325
-
326
- This is a higher-level method that makes it clear we're exporting values.
327
- It first tries to use the ssm_exports property, then falls back to ssm_parameters.
328
-
329
- Args:
330
- scope: The CDK construct scope
331
- resource_values: Dictionary of resource values to export
332
- config: Configuration object with ssm_exports or ssm_parameters
333
- resource_name: Name of the resource (used as prefix for parameter IDs)
334
- resource_type: Type of the resource (e.g., 'vpc', 'security-group')
335
- context: Additional context variables for template formatting
336
-
337
- Returns:
338
- Dictionary of created SSM parameters
339
- """
340
- # First try the new ssm_exports property
341
- ssm_config = getattr(config, "ssm_exports", {})
342
-
343
- # If empty, fall back to the legacy ssm_parameters for backward compatibility
344
- if not ssm_config:
345
- ssm_config = getattr(config, "ssm_parameters", {})
346
-
347
- # Export all resources to SSM if paths are configured
348
- if ssm_config:
349
- logger.info(f"Exporting resources to SSM: {list(ssm_config.keys())}")
350
-
351
- # Format the SSM paths using the template if available
352
- formatted_ssm_config = {}
353
- for key, path in ssm_config.items():
354
- # Extract the attribute name from the key (remove _path suffix)
355
- attr_name = key[:-5] if key.endswith("_path") else key
356
-
357
- # If config has format_ssm_path method, use it to format the path
358
- if hasattr(config, "format_ssm_path") and resource_type:
359
- formatted_path = config.format_ssm_path(
360
- path=path,
361
- resource_type=resource_type,
362
- resource_name=resource_name,
363
- attribute=attr_name,
364
- context=context,
365
- )
366
- formatted_ssm_config[key] = formatted_path
367
- else:
368
- formatted_ssm_config[key] = path
369
-
370
- return self.export_ssm_parameters_from_config(
371
- scope=scope,
372
- config_dict=resource_values,
373
- ssm_config=formatted_ssm_config,
374
- resource=f"{resource_name}-",
375
- )
376
- else:
377
- logger.info(f"No SSM export paths configured for {resource_name} resources")
378
- logger.info("The following SSM exports are available for this resource: ")
379
- for key, item in resource_values.items():
380
- logger.info(f"{key}: {item}")
381
- return {}
382
-
383
- def import_resources_from_ssm(
384
- self,
385
- scope: Construct,
386
- config: Any, # Should be a BaseConfig subclass
387
- resource_name: str,
388
- resource_type: str = None,
389
- context: Dict[str, Any] = None,
390
- ) -> Dict[str, str]:
391
- """
392
- Import resource attributes from SSM Parameter Store based on configuration.
393
-
394
- This is a higher-level method that makes it clear we're importing values.
395
- It first tries to use the ssm_imports property, then falls back to ssm_parameters.
396
-
397
- Enhanced to also cache results for easy access via get_ssm_imported_value().
398
-
399
- Args:
400
- scope: The CDK construct scope
401
- config: Configuration object with ssm_imports or ssm_parameters
402
- resource_name: Name of the resource (used as prefix for parameter IDs)
403
- resource_type: Type of the resource (e.g., 'vpc', 'security-group')
404
- context: Additional context variables for template formatting
405
-
406
- Returns:
407
- Dictionary of imported SSM parameter values
408
- """
409
- # First try the new ssm_imports property
410
- ssm_config = getattr(config, "ssm_imports", {})
411
-
412
- # If empty, fall back to the legacy ssm_parameters for backward compatibility
413
- if not ssm_config:
414
- ssm_config = getattr(config, "ssm_parameters", {})
415
-
416
- imported_values = {}
417
-
418
- if ssm_config:
419
- logger.info(f"Importing resources from SSM: {list(ssm_config.keys())}")
420
- for key, path in ssm_config.items():
421
- if not path:
422
- continue
423
-
424
- # Extract the attribute name from the key (remove _path suffix)
425
- attr_name = key[:-5] if key.endswith("_path") else key
426
-
427
- # Format the SSM path using the template if available
428
- if hasattr(config, "format_ssm_path") and resource_type:
429
- formatted_path = config.format_ssm_path(
430
- path=path,
431
- resource_type=resource_type,
432
- resource_name=resource_name,
433
- attribute=attr_name,
434
- context=context,
435
- )
436
- else:
437
- formatted_path = path
438
-
439
- id_name = f"{resource_name}-{key.replace('_', '')}-Import"
440
- value = self.import_ssm_parameter(
441
- scope=scope, id=id_name, parameter_name=formatted_path
442
- )
443
-
444
- if value:
445
- # Remove _path suffix if present
446
- final_key = key[:-5] if key.endswith("_path") else key
447
- imported_values[final_key] = value
448
-
449
- # Also cache for easy access via get_ssm_imported_value()
450
- self._ssm_imported_values[final_key] = value
451
- else:
452
- logger.info(f"No SSM import paths configured for {resource_name} resources")
453
-
454
- return imported_values