cdk-factory 0.7.30__py3-none-any.whl → 0.8.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.
@@ -70,10 +70,13 @@ class CdkConfig:
70
70
  relative_config_path = ""
71
71
  # is this a relative path or a real path
72
72
  if not config_path.startswith("."):
73
-
74
- root_path = os.path.commonpath([config_path, runtime_directory])
75
- if root_path in config_path:
76
- relative_config_path = config_path.replace(root_path, ".")
73
+ # Ensure both paths are absolute to avoid mixing absolute and relative paths
74
+ abs_config_path = os.path.abspath(config_path)
75
+ abs_runtime_directory = os.path.abspath(runtime_directory)
76
+
77
+ root_path = os.path.commonpath([abs_config_path, abs_runtime_directory])
78
+ if root_path in abs_config_path:
79
+ relative_config_path = abs_config_path.replace(root_path, ".")
77
80
 
78
81
  print(f"👉 Relative Config: {relative_config_path}")
79
82
  else:
@@ -55,10 +55,15 @@ class EnhancedBaseConfig(BaseConfig):
55
55
  """Check if SSM parameter integration is enabled"""
56
56
  return self._enhanced_ssm.enabled if self._enhanced_ssm else False
57
57
 
58
+ @property
59
+ def ssm_workload(self) -> str:
60
+ """Get the workload name for SSM parameter paths"""
61
+ return self._enhanced_ssm.workload if self._enhanced_ssm else "cdk-factory"
62
+
58
63
  @property
59
64
  def ssm_organization(self) -> str:
60
- """Get the organization name for SSM parameter paths"""
61
- return self._enhanced_ssm.organization if self._enhanced_ssm else "cdk-factory"
65
+ """Deprecated: Use ssm_workload instead. Kept for backward compatibility."""
66
+ return self.ssm_workload
62
67
 
63
68
  @property
64
69
  def ssm_environment(self) -> str:
@@ -68,7 +73,7 @@ class EnhancedBaseConfig(BaseConfig):
68
73
  @property
69
74
  def ssm_pattern(self) -> str:
70
75
  """Get the SSM parameter path pattern"""
71
- return self._enhanced_ssm.pattern if self._enhanced_ssm else "/{organization}/{environment}/{stack_type}/{resource_name}/{attribute}"
76
+ return self._enhanced_ssm.pattern if self._enhanced_ssm else "/{workload}/{environment}/{stack_type}/{resource_name}/{attribute}"
72
77
 
73
78
  @property
74
79
  def ssm_auto_export(self) -> bool:
@@ -98,7 +103,7 @@ class EnhancedBaseConfig(BaseConfig):
98
103
  # Fallback to simple path generation
99
104
  if custom_path and custom_path.startswith("/"):
100
105
  return custom_path
101
- return f"/{self.ssm_organization}/{self.ssm_environment}/{attribute}"
106
+ return f"/{self.ssm_workload}/{self.ssm_environment}/{attribute}"
102
107
 
103
108
  def get_export_definitions(self, context: Dict[str, Any] = None) -> List[SsmParameterDefinition]:
104
109
  """
@@ -42,8 +42,10 @@ class EnhancedSsmConfig:
42
42
  return self.config.get("enabled", True)
43
43
 
44
44
  @property
45
- def organization(self) -> str:
46
- return self.config.get("organization", "default")
45
+ def workload(self) -> str:
46
+ """Get workload name for SSM parameter paths (backward compatible with 'organization')"""
47
+ # Try 'workload' first, fall back to 'organization' for backward compatibility
48
+ return self.config.get("workload", self.config.get("organization", "default"))
47
49
 
48
50
  @property
49
51
  def environment(self) -> str:
@@ -58,7 +60,7 @@ class EnhancedSsmConfig:
58
60
  def pattern(self) -> str:
59
61
  return self.config.get(
60
62
  "pattern",
61
- "/{organization}/{environment}/{stack_type}/{resource_name}/{attribute}",
63
+ "/{workload}/{environment}/{stack_type}/{resource_name}/{attribute}",
62
64
  )
63
65
 
64
66
 
@@ -90,9 +92,10 @@ class EnhancedSsmConfig:
90
92
  # Convert underscore attribute names to hyphen format for consistent SSM paths
91
93
  formatted_attribute = attribute.replace("_", "-")
92
94
 
93
- # Use enhanced pattern
95
+ # Use enhanced pattern (support both workload and organization for backward compatibility)
94
96
  return self.pattern.format(
95
- organization=self.organization,
97
+ workload=self.workload,
98
+ organization=self.workload, # Backward compatibility
96
99
  environment=self.environment,
97
100
  stack_type=self.resource_type,
98
101
  resource_name=self.resource_name,
@@ -236,7 +239,7 @@ class EnhancedSsmConfig:
236
239
  formatted_resource_name = source_resource_name.replace("_", "-")
237
240
  formatted_resource_type = source_resource_type.replace("_", "-")
238
241
 
239
- return f"/{self.organization}/{self.environment}/{formatted_resource_type}/{formatted_resource_name}/{formatted_attribute}"
242
+ return f"/{self.workload}/{self.environment}/{formatted_resource_type}/{formatted_resource_name}/{formatted_attribute}"
240
243
 
241
244
 
242
245
  # Resource type definitions for auto-discovery
@@ -190,15 +190,23 @@ class CloudFrontDistributionConstruct(Construct):
190
190
  cloudfront_config = self.stack_config.dictionary.get("cloudfront", {})
191
191
  enable_url_rewrite = cloudfront_config.get("enable_url_rewrite", False)
192
192
 
193
- if enable_url_rewrite:
193
+ # CloudFront only allows ONE function per event type
194
+ # If both URL rewrite and host restrictions are needed, combine them
195
+ if enable_url_rewrite and self.restrict_to_known_hosts and self.aliases:
196
+ function_associations.append(
197
+ cloudfront.FunctionAssociation(
198
+ function=self.__get_combined_function(hosts=self.aliases),
199
+ event_type=cloudfront.FunctionEventType.VIEWER_REQUEST,
200
+ )
201
+ )
202
+ elif enable_url_rewrite:
194
203
  function_associations.append(
195
204
  cloudfront.FunctionAssociation(
196
205
  function=self.__get_url_rewrite_function(),
197
206
  event_type=cloudfront.FunctionEventType.VIEWER_REQUEST,
198
207
  )
199
208
  )
200
-
201
- if self.restrict_to_known_hosts and self.aliases:
209
+ elif self.restrict_to_known_hosts and self.aliases:
202
210
  function_associations.append(
203
211
  cloudfront.FunctionAssociation(
204
212
  function=self.__get_cloudfront_host_restrictions(
@@ -210,6 +218,58 @@ class CloudFrontDistributionConstruct(Construct):
210
218
 
211
219
  return function_associations
212
220
 
221
+ def __get_combined_function(self, hosts: List[str]) -> cloudfront.Function:
222
+ """
223
+ Creates a combined CloudFront function that does both URL rewriting and host restrictions.
224
+ This is necessary because CloudFront only allows one function per event type.
225
+
226
+ Args:
227
+ hosts: List of allowed hostnames
228
+
229
+ Returns:
230
+ cloudfront.Function: Combined function
231
+ """
232
+ allowed_hosts = "[" + ", ".join(f"'{host}'" for host in hosts) + "]"
233
+
234
+ function_code = f"""
235
+ function handler(event) {{
236
+ var request = event.request;
237
+ var allowedHosts = {allowed_hosts};
238
+ var hostHeader = request.headers.host.value;
239
+
240
+ // Check host restrictions first
241
+ if (allowedHosts.indexOf(hostHeader) === -1) {{
242
+ return {{ statusCode: 403, statusDescription: 'Forbidden' }};
243
+ }}
244
+
245
+ // Then do URL rewrite
246
+ var uri = request.uri;
247
+
248
+ // If URI doesn't have a file extension and doesn't end with /
249
+ if (!uri.includes('.') && !uri.endsWith('/')) {{
250
+ request.uri = uri + '/index.html';
251
+ }}
252
+ // If URI ends with / but not index.html
253
+ else if (uri.endsWith('/') && !uri.endsWith('index.html')) {{
254
+ request.uri = uri + 'index.html';
255
+ }}
256
+ // If URI is exactly /
257
+ else if (uri === '/') {{
258
+ request.uri = '/index.html';
259
+ }}
260
+
261
+ return request;
262
+ }}
263
+ """
264
+
265
+ combined_function = cloudfront.Function(
266
+ self,
267
+ "CombinedFunction",
268
+ comment="Combined URL rewrite and host restrictions for static site routing",
269
+ code=cloudfront.FunctionCode.from_inline(function_code),
270
+ )
271
+ return combined_function
272
+
213
273
  def __get_url_rewrite_function(self) -> cloudfront.Function:
214
274
  """
215
275
  Creates a CloudFront function that rewrites URLs for SPA/static site routing.
@@ -316,10 +316,112 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
316
316
  def _setup_lambda_routes(self, api_gateway, api_id, routes, authorizer):
317
317
  """Setup Lambda routes and integrations"""
318
318
  for route in routes:
319
- self._setup_single_lambda_route(api_gateway, api_id, route, authorizer)
319
+ # Check if this route references an existing Lambda via SSM
320
+ lambda_arn_ssm_path = route.get("lambda_arn_ssm_path")
321
+ lambda_name_ref = route.get("lambda_name")
322
+
323
+ if lambda_arn_ssm_path or lambda_name_ref:
324
+ # Import existing Lambda from SSM
325
+ self._setup_existing_lambda_route(api_gateway, api_id, route, authorizer)
326
+ else:
327
+ # Create new Lambda (legacy pattern)
328
+ self._setup_single_lambda_route(api_gateway, api_id, route, authorizer)
329
+
330
+ def _setup_existing_lambda_route(self, api_gateway, api_id, route, authorizer):
331
+ """
332
+ Setup API Gateway route with existing Lambda function imported from SSM.
333
+ This is the NEW PATTERN for separating Lambda and API Gateway stacks.
334
+ """
335
+ route_path = route["path"]
336
+ suffix = route_path.strip("/").replace("/", "-") or "health"
337
+
338
+ # Get Lambda ARN from SSM Parameter Store
339
+ lambda_arn = self._get_lambda_arn_from_ssm(route)
340
+
341
+ if not lambda_arn:
342
+ raise ValueError(
343
+ f"Could not resolve Lambda ARN for route {route_path}. "
344
+ f"Ensure Lambda stack has deployed and exported ARN to SSM."
345
+ )
346
+
347
+ # Import Lambda function from ARN
348
+ lambda_fn = _lambda.Function.from_function_arn(
349
+ self,
350
+ f"{api_id}-imported-lambda-{suffix}",
351
+ lambda_arn
352
+ )
353
+
354
+ logger.info(f"Imported Lambda for route {route_path}: {lambda_arn}")
355
+
356
+ # Setup API Gateway resource
357
+ resource = (
358
+ api_gateway.root.resource_for_path(route_path)
359
+ if route_path != "/"
360
+ else api_gateway.root
361
+ )
362
+
363
+ # Setup Lambda integration
364
+ self._setup_lambda_integration(
365
+ api_gateway, api_id, route, lambda_fn, authorizer, suffix
366
+ )
367
+
368
+ # Setup CORS using centralized utility
369
+ self.integration_utility.setup_route_cors(resource, route_path, route)
370
+
371
+ def _get_lambda_arn_from_ssm(self, route: dict) -> str:
372
+ """
373
+ Get Lambda ARN from SSM Parameter Store.
374
+ Supports both explicit SSM paths and auto-discovery via lambda_name.
375
+ """
376
+ # Option 1: Explicit SSM path provided
377
+ lambda_arn_ssm_path = route.get("lambda_arn_ssm_path")
378
+ if lambda_arn_ssm_path:
379
+ logger.info(f"Looking up Lambda ARN from SSM: {lambda_arn_ssm_path}")
380
+ try:
381
+ param = ssm.StringParameter.from_string_parameter_name(
382
+ self,
383
+ f"lambda-arn-param-{hash(lambda_arn_ssm_path) % 10000}",
384
+ lambda_arn_ssm_path
385
+ )
386
+ return param.string_value
387
+ except Exception as e:
388
+ logger.error(f"Failed to retrieve Lambda ARN from SSM path {lambda_arn_ssm_path}: {e}")
389
+ raise
390
+
391
+ # Option 2: Auto-discovery via lambda_name
392
+ lambda_name = route.get("lambda_name")
393
+ if lambda_name:
394
+ # Build SSM path using convention from lambda_stack
395
+ ssm_imports_config = self.stack_config.dictionary.get("api_gateway", {}).get("ssm", {}).get("imports", {})
396
+ # Try 'workload' first, fall back to 'organization' for backward compatibility
397
+ workload = ssm_imports_config.get("workload", ssm_imports_config.get("organization", self.deployment.workload_name))
398
+ environment = ssm_imports_config.get("environment", self.deployment.environment)
399
+
400
+ ssm_path = f"/{workload}/{environment}/lambda/{lambda_name}/arn"
401
+ logger.info(f"Auto-discovering Lambda ARN from SSM: {ssm_path}")
402
+
403
+ try:
404
+ param = ssm.StringParameter.from_string_parameter_name(
405
+ self,
406
+ f"lambda-arn-{lambda_name}-param",
407
+ ssm_path
408
+ )
409
+ return param.string_value
410
+ except Exception as e:
411
+ logger.error(f"Failed to auto-discover Lambda ARN for '{lambda_name}' from {ssm_path}: {e}")
412
+ raise ValueError(
413
+ f"Lambda ARN not found in SSM for '{lambda_name}'. "
414
+ f"Ensure the Lambda stack has deployed and exported the ARN to: {ssm_path}"
415
+ )
416
+
417
+ return None
320
418
 
321
419
  def _setup_single_lambda_route(self, api_gateway, api_id, route, authorizer):
322
- """Setup a single Lambda route with integration and CORS"""
420
+ """
421
+ Setup a single Lambda route with integration and CORS.
422
+ LEGACY PATTERN: Creates a new Lambda function inline.
423
+ Prefer _setup_existing_lambda_route for new implementations.
424
+ """
323
425
  suffix = route["path"].strip("/").replace("/", "-") or "health"
324
426
  src = route.get("src")
325
427
  handler = route.get("handler")
@@ -34,11 +34,7 @@ from cdk_factory.configurations.resources.lambda_function import (
34
34
  SQS as SQSConfig,
35
35
  )
36
36
 
37
- from cdk_factory.utilities.api_gateway_integration_utility import (
38
- ApiGatewayIntegrationUtility,
39
- )
40
- from aws_cdk import aws_apigateway as apigateway
41
- from aws_cdk import aws_cognito as cognito
37
+ from aws_cdk import aws_ssm as ssm
42
38
 
43
39
  from cdk_factory.utilities.docker_utilities import DockerUtilities
44
40
  from cdk_factory.stack.stack_module_registry import register_stack
@@ -71,8 +67,7 @@ class LambdaStack(IStack):
71
67
  self.stack_config: StackConfig | None = None
72
68
  self.deployment: DeploymentConfig | None = None
73
69
  self.workload: WorkloadConfig | None = None
74
- self.api_gateway_integrations: list = []
75
- self.integration_utility = None
70
+ self.exported_lambda_arns: dict = {} # Store exported Lambda ARNs
76
71
 
77
72
  self.__nag_rule_suppressions()
78
73
 
@@ -90,8 +85,9 @@ class LambdaStack(IStack):
90
85
  self.deployment = deployment
91
86
  self.workload = workload
92
87
 
93
- # Initialize integration utility for consistent API Gateway behavior
94
- self.integration_utility = ApiGatewayIntegrationUtility(self)
88
+ # Check for deprecated API Gateway configuration in lambda resources
89
+ self.__check_for_deprecated_api_config(stack_config)
90
+
95
91
  resources = stack_config.dictionary.get("resources", [])
96
92
  if len(resources) == 0:
97
93
  resources = stack_config.dictionary.get("lambdas", [])
@@ -100,14 +96,13 @@ class LambdaStack(IStack):
100
96
 
101
97
  lambda_functions: List[LambdaFunctionConfig] = []
102
98
  for resource in resources:
103
-
104
99
  config = LambdaFunctionConfig(config=resource, deployment=deployment)
105
100
  lambda_functions.append(config)
106
101
 
107
102
  self.functions = self.__setup_lambdas(lambda_functions)
108
103
 
109
- # Trigger API Gateway deployment after all integrations are added
110
- self.__finalize_api_gateway_deployments()
104
+ # Export Lambda ARNs to SSM Parameter Store for API Gateway integration
105
+ self.__export_lambda_arns_to_ssm()
111
106
 
112
107
  def __nag_rule_suppressions(self):
113
108
  pass
@@ -191,12 +186,12 @@ class LambdaStack(IStack):
191
186
  f"A resource policy for {rp.get('principal')} has not been defined"
192
187
  )
193
188
 
194
- # Handle API Gateway integration if configured
195
- if function_config.api:
196
- self.__setup_api_gateway_integration(
197
- lambda_function=lambda_function,
198
- function_config=function_config,
199
- )
189
+ # Store Lambda function for SSM export
190
+ self.exported_lambda_arns[function_config.name] = {
191
+ "arn": lambda_function.function_arn,
192
+ "name": lambda_function.function_name,
193
+ "function": lambda_function,
194
+ }
200
195
 
201
196
  functions.append(lambda_function)
202
197
 
@@ -299,66 +294,117 @@ class LambdaStack(IStack):
299
294
 
300
295
  rule.add_target(aws_events_targets.LambdaFunction(lambda_function))
301
296
 
302
- def __setup_api_gateway_integration(
303
- self, lambda_function: _lambda.Function, function_config: LambdaFunctionConfig
304
- ) -> None:
305
- """Setup API Gateway integration for Lambda function using shared utility"""
306
- api_config = function_config.api
307
-
308
- if not api_config:
309
- raise ValueError("API Gateway config is missing in Lambda function config")
310
-
311
- # Get or create API Gateway using shared utility
312
- api_gateway = self.integration_utility.get_or_create_api_gateway(
313
- api_config, self.stack_config, self.api_gateway_integrations
314
- )
315
-
316
- # Setup the integration using shared utility
317
- integration_info = self.integration_utility.setup_lambda_integration(
318
- lambda_function, api_config, api_gateway, self.stack_config
319
- )
320
-
321
- # Setup CORS for the route using shared utility
322
- route_config = {"cors": api_config.cors} if hasattr(api_config, 'cors') and api_config.cors else {}
323
- if route_config.get("cors"):
324
- self.integration_utility.setup_route_cors(
325
- integration_info["resource"],
326
- api_config.routes,
327
- route_config
328
- )
329
-
330
- # Store integration info for potential cross-stack references
331
- integration_info["function_name"] = function_config.name
332
- self.api_gateway_integrations.append(integration_info)
333
-
334
- logger.info(f"Created API Gateway integration for {function_config.name}")
335
-
336
- def __finalize_api_gateway_deployments(self):
337
- """Create deployments and stages for API Gateways after all integrations are added"""
338
- if not self.api_gateway_integrations:
297
+ def __check_for_deprecated_api_config(self, stack_config: StackConfig) -> None:
298
+ """
299
+ Check for deprecated API Gateway configuration in lambda resources.
300
+ Raises an error with migration guidance if found.
301
+ """
302
+ resources = stack_config.dictionary.get("resources", [])
303
+ if not resources:
304
+ resources = stack_config.dictionary.get("lambdas", [])
305
+
306
+ deprecated_configs = []
307
+ for resource in resources:
308
+ # Check for 'api' key in resource configuration
309
+ if "api" in resource or "api_gateway" in stack_config.dictionary:
310
+ deprecated_configs.append(resource.get("name", "unnamed"))
311
+
312
+ if deprecated_configs:
313
+ error_msg = f"""
314
+ ╔══════════════════════════════════════════════════════════════════════════════╗
315
+ ║ 🚨 DEPRECATED CONFIGURATION DETECTED ║
316
+ ╚══════════════════════════════════════════════════════════════════════════════╝
317
+
318
+ Your lambda_stack configuration includes API Gateway setup, which is now
319
+ deprecated. This pattern caused deployment issues and violated AWS best practices.
320
+
321
+ 📋 AFFECTED RESOURCES:
322
+ {chr(10).join(f" • {name}" for name in deprecated_configs)}
323
+
324
+ 🔧 REQUIRED MIGRATION (Breaking Change v2.0+):
325
+
326
+ Lambda stacks should ONLY create Lambda functions. API Gateway integration
327
+ should be handled in a separate api_gateway_stack.
328
+
329
+ NEW PATTERN:
330
+ ┌─────────────────────────────────────────────────────────────────────────────┐
331
+ 1. Lambda Stack (lambda_stack)
332
+ │ • Creates Lambda functions │
333
+ │ • Exports Lambda ARNs to SSM Parameter Store │
334
+ │ • NO API Gateway configuration │
335
+ │ │
336
+ │ 2. API Gateway Stack (api_gateway_stack) │
337
+ │ • Imports Lambda ARNs from SSM │
338
+ │ • Creates API Gateway │
339
+ │ • Wires Lambda integrations │
340
+ │ • Manages stages, deployments, and domains │
341
+ └─────────────────────────────────────────────────────────────────────────────┘
342
+
343
+ 📚 MIGRATION GUIDE:
344
+ https://github.com/geek-cafe/cdk-factory/blob/main/docs/MIGRATION_V2.md
345
+
346
+ 💡 QUICK FIX:
347
+ 1. Remove 'api' configuration from lambda resources
348
+ 2. Remove 'api_gateway' from stack config
349
+ 3. Create new api_gateway_stack configuration
350
+ 4. Update pipeline: lambdas deploy first, then api_gateway
351
+
352
+ ❓ NEED HELP?
353
+ See examples: cdk-factory/examples/separate-api-gateway/
354
+
355
+ ════════════════════════════════════════════════════════════════════════════════
356
+ """
357
+ raise ValueError(error_msg)
358
+
359
+ def __export_lambda_arns_to_ssm(self) -> None:
360
+ """
361
+ Export Lambda ARNs to SSM Parameter Store for cross-stack references.
362
+ This enables the API Gateway stack to import and integrate with these Lambdas.
363
+ """
364
+ if not self.exported_lambda_arns:
365
+ logger.info("No Lambda functions to export to SSM")
339
366
  return
340
367
 
341
- # Use consolidated utility for deployment and stage creation
342
- api_gateway = self.api_gateway_integrations[0].get("api_gateway")
368
+ # Get SSM export configuration
369
+ ssm_config = self.stack_config.dictionary.get("ssm", {})
370
+ if not ssm_config.get("enabled", False):
371
+ logger.info("SSM export is not enabled for this stack")
372
+ return
343
373
 
344
- # Use consolidated deployment and stage creation
345
- stage = self.integration_utility.finalize_api_gateway_deployment(
346
- api_gateway=api_gateway,
347
- integrations=self.api_gateway_integrations,
348
- stack_config=self.stack_config,
349
- api_config=None, # Lambda stack doesn't have ApiGatewayConfig
350
- construct_scope=self,
351
- counter=1
352
- )
353
-
354
- def _get_api_gateway_stage_name(self) -> str:
355
- """Get the API Gateway stage name from configuration"""
356
- # Check if there's an API Gateway configuration in the stack config
357
- api_gateway_config = self.stack_config.dictionary.get("api_gateway", {})
358
- name = api_gateway_config.get("stage", {}).get("name")
359
- if name:
360
- return name
361
- return api_gateway_config.get("stage_name", "prod")
374
+ # Build SSM parameter prefix
375
+ # Try 'workload' first, fall back to 'organization' for backward compatibility
376
+ workload = ssm_config.get("workload", ssm_config.get("organization", self.deployment.workload_name))
377
+ environment = ssm_config.get("environment", self.deployment.environment)
378
+ prefix = f"/{workload}/{environment}/lambda"
379
+
380
+ logger.info(f"Exporting {len(self.exported_lambda_arns)} Lambda functions to SSM under {prefix}")
381
+
382
+ for lambda_name, lambda_info in self.exported_lambda_arns.items():
383
+ # Create SSM parameter for Lambda ARN
384
+ param_name = f"{prefix}/{lambda_name}/arn"
385
+ ssm.StringParameter(
386
+ self,
387
+ f"ssm-export-{lambda_name}-arn",
388
+ parameter_name=param_name,
389
+ string_value=lambda_info["arn"],
390
+ description=f"Lambda ARN for {lambda_name}",
391
+ tier=ssm.ParameterTier.STANDARD,
392
+ )
393
+
394
+ # Also export function name for convenience
395
+ param_name_fname = f"{prefix}/{lambda_name}/function-name"
396
+ ssm.StringParameter(
397
+ self,
398
+ f"ssm-export-{lambda_name}-name",
399
+ parameter_name=param_name_fname,
400
+ string_value=lambda_info["name"],
401
+ description=f"Lambda function name for {lambda_name}",
402
+ tier=ssm.ParameterTier.STANDARD,
403
+ )
404
+
405
+ logger.info(f"✅ Exported Lambda '{lambda_name}' to SSM: {param_name}")
406
+
407
+ print(f"📤 Exported {len(self.exported_lambda_arns)} Lambda function(s) to SSM Parameter Store")
362
408
 
363
409
  def __setup_lambda_docker_file(
364
410
  self, lambda_config: LambdaFunctionConfig
cdk_factory/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.7.30"
1
+ __version__ = "0.8.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cdk_factory
3
- Version: 0.7.30
3
+ Version: 0.8.0
4
4
  Summary: CDK Factory. A QuickStarter and best practices setup for CDK projects
5
5
  Author-email: Eric Wilson <eric.wilson@geekcafe.com>
6
6
  License: MIT License
@@ -1,16 +1,16 @@
1
1
  cdk_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  cdk_factory/app.py,sha256=xv863N7O6HPKznB68_t7O4la9JacrkG87t9TjoDUk7s,2827
3
3
  cdk_factory/cdk.json,sha256=SKZKhJ2PBpFH78j-F8S3VDYW-lf76--Q2I3ON-ZIQfw,3106
4
- cdk_factory/version.py,sha256=4c9Y6kqbBff_PtGI0UeOh2f4HPFaCOvIx0KdnuUqM7c,23
4
+ cdk_factory/version.py,sha256=iPlYCcIzuzW7T2HKDkmYlMkRI51dBLfNRxPPiWrfw9U,22
5
5
  cdk_factory/builds/README.md,sha256=9BBWd7bXpyKdMU_g2UljhQwrC9i5O_Tvkb6oPvndoZk,90
6
6
  cdk_factory/commands/command_loader.py,sha256=QbLquuP_AdxtlxlDy-2IWCQ6D-7qa58aphnDPtp_uTs,3744
7
7
  cdk_factory/configurations/base_config.py,sha256=JKjhNsy0RCUZy1s8n5D_aXXI-upR9izaLtCTfKYiV9k,9624
8
- cdk_factory/configurations/cdk_config.py,sha256=ex7NYvgEu78cfyiiP81cKtlhZZu2PSzJesSWGADpDh8,8549
8
+ cdk_factory/configurations/cdk_config.py,sha256=eYboaXcz9CUx4rJbEagX8ppgtIERUlopcJXii7Vn8Ws,8796
9
9
  cdk_factory/configurations/deployment.py,sha256=LO2gd1yv1nqlX_2MIjRXptylFybWiTKsMKZU0N58rSM,12110
10
10
  cdk_factory/configurations/deployment_wave.py,sha256=TFX7CYgr5SmLyziEb-R_OTteFWtlMHB4pT53ekV3d1Y,233
11
11
  cdk_factory/configurations/devops.py,sha256=PG-s2ldZmMULheWdKf2lf2LSugLoKiOKyLELTZJJxu8,2506
12
- cdk_factory/configurations/enhanced_base_config.py,sha256=4QWNaUYDaHkIIIzRMYwsgzKhV7ERcLjhA0A6tqSq9sU,6498
13
- cdk_factory/configurations/enhanced_ssm_config.py,sha256=mJRNMOLQ-75MdXuRZcXqmEHN6syeU4j1oYfSfJhzIWg,12390
12
+ cdk_factory/configurations/enhanced_base_config.py,sha256=Y1gcGZxyf_O2KFnVpCBORSGHWHqHxw0vNx_ijMad_QA,6654
13
+ cdk_factory/configurations/enhanced_ssm_config.py,sha256=8TRZX-gsv_fYjMKufAe2_-iF76RRE3grkC2MTYQxiJo,12718
14
14
  cdk_factory/configurations/management.py,sha256=TSOIyxO9hGNxbgiTsS8a3pz03ungXiNqPPtZtfOpr8M,1373
15
15
  cdk_factory/configurations/pipeline.py,sha256=3RmRP1GIk42rjYZ-A9H3357RcO13IA47N-2IQcBkySQ,4939
16
16
  cdk_factory/configurations/pipeline_stage.py,sha256=eAT-FoIepIuv5tObk4TXlCN47FaatQO2rrFchgbMdXU,3415
@@ -46,7 +46,7 @@ cdk_factory/configurations/resources/security_group.py,sha256=8kQtaaRVEn2aDm8XoC
46
46
  cdk_factory/configurations/resources/security_group_full_stack.py,sha256=x5MIMCa_olO7prFBKx9zVOfvsVdKo-2mWyhrCy27dFw,2031
47
47
  cdk_factory/configurations/resources/sqs.py,sha256=fAh2dqttJ6PX46enFRULuiLEu3TEj0Vb2xntAOgUpYE,4346
48
48
  cdk_factory/configurations/resources/vpc.py,sha256=sNn6w76bHFwmt6N76gZZhqpsuNB9860C1SZu6tebaXY,3835
49
- cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py,sha256=84RF7eH7LW2fkqJIQmH3kI1xTb5vbdZShE-0Xb65Gek,12194
49
+ cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py,sha256=7uDkDbFigeUOuYgsQ9ghA4dGlx63rUCa6Iiq4RWokEA,14627
50
50
  cdk_factory/constructs/ecr/ecr_construct.py,sha256=s0egnh4U7qgCSn_LLq1soRLWSxsFjrTx8P591AjkN84,7025
51
51
  cdk_factory/constructs/lambdas/lambda_function_construct.py,sha256=SQ5SEXn4kezVAzXuv_A_JB3o_svyBXOMi-htvfB9HQs,4516
52
52
  cdk_factory/constructs/lambdas/lambda_function_docker_construct.py,sha256=aSyn3eh1YnuIahZ7CbZ5WswwPL8u70ZibMoS24QQifc,9907
@@ -73,10 +73,10 @@ cdk_factory/stack/stack_module_registry.py,sha256=J14-A75VZESzRQa8p-Fepdap7Z8T7m
73
73
  cdk_factory/stack/stack_modules.py,sha256=kgEK-j0smZPozVwTCfM1g1V17EyTBT0TXAQZq4vZz0o,784
74
74
  cdk_factory/stack_library/__init__.py,sha256=5Y9TpIe8ZK1688G60PGcuP-hM0RvYEY_3Hl2qJCJJrw,581
75
75
  cdk_factory/stack_library/stack_base.py,sha256=tTleSFmlf26DuKVF_ytftf8P7IVWb5iex8cYfYupfvQ,4940
76
- cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=83oV1pSFfb68mH4CH3mz0SERiiihpbPCsKK9zHgjvyE,32490
76
+ cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=ekfu1BaA_b-Yn7U1SlfGLyskTQXMPjjotZDkxqYeysk,36984
77
77
  cdk_factory/stack_library/auto_scaling/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
78
78
  cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py,sha256=UsFqUb_3XPJAlmZ6F75nXna3elOggD1KuFmmdmhi0Lg,19070
79
- cdk_factory/stack_library/aws_lambdas/lambda_stack.py,sha256=xT8-799fSXMpYaRhRKytxsbpb5EG3g2h6L822_RpRSk,22956
79
+ cdk_factory/stack_library/aws_lambdas/lambda_stack.py,sha256=SFbBPvvCopbyiuYtq-O5sQkFCf94Wzua6aDUXiFDSB4,26161
80
80
  cdk_factory/stack_library/buckets/README.md,sha256=XkK3UNVtRLE7NtUvbhCOBBYUYi8hlrrSaI1s3GJVrqI,78
81
81
  cdk_factory/stack_library/buckets/bucket_stack.py,sha256=SLoZqSffAqmeBBEVUQg54D_8Ad5UKdkjEAmKAVgAqQo,1778
82
82
  cdk_factory/stack_library/code_artifact/code_artifact_stack.py,sha256=vySYIjWGTdVfMcUOyJdW6gTL1maHWq9ThzfrN_rVL5A,6290
@@ -112,7 +112,7 @@ cdk_factory/utilities/lambda_function_utilities.py,sha256=j3tBdv_gC2MdEwBINDwAqY
112
112
  cdk_factory/utilities/os_execute.py,sha256=5Op0LY_8Y-pUm04y1k8MTpNrmQvcLmQHPQITEP7EuSU,1019
113
113
  cdk_factory/utils/api_gateway_utilities.py,sha256=If7Xu5s_UxmuV-kL3JkXxPLBdSVUKoLtohm0IUFoiV8,4378
114
114
  cdk_factory/workload/workload_factory.py,sha256=yBUDGIuB8-5p_mGcVFxsD2ZoZIziak3yh3LL3JvS0M4,5903
115
- cdk_factory-0.7.30.dist-info/METADATA,sha256=DHv28uW3uNKA65mnxlQXO3PQxnoaaYAdtPlTZuaM-9E,2451
116
- cdk_factory-0.7.30.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
117
- cdk_factory-0.7.30.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
118
- cdk_factory-0.7.30.dist-info/RECORD,,
115
+ cdk_factory-0.8.0.dist-info/METADATA,sha256=74xL4nv4hBAxFNfxS3OTUkb91bBl3ooiLVwxMgQpPqc,2450
116
+ cdk_factory-0.8.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
117
+ cdk_factory-0.8.0.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
118
+ cdk_factory-0.8.0.dist-info/RECORD,,