cdk-factory 0.7.31__py3-none-any.whl → 0.8.1__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.
- cdk_factory/configurations/cdk_config.py +7 -4
- cdk_factory/configurations/enhanced_base_config.py +9 -4
- cdk_factory/configurations/enhanced_ssm_config.py +12 -7
- cdk_factory/stack_library/api_gateway/api_gateway_stack.py +124 -3
- cdk_factory/stack_library/aws_lambdas/lambda_stack.py +121 -75
- cdk_factory/utilities/api_gateway_integration_utility.py +7 -0
- cdk_factory/version.py +1 -1
- {cdk_factory-0.7.31.dist-info → cdk_factory-0.8.1.dist-info}/METADATA +1 -1
- {cdk_factory-0.7.31.dist-info → cdk_factory-0.8.1.dist-info}/RECORD +11 -11
- {cdk_factory-0.7.31.dist-info → cdk_factory-0.8.1.dist-info}/WHEEL +0 -0
- {cdk_factory-0.7.31.dist-info → cdk_factory-0.8.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
"""
|
|
61
|
-
return self.
|
|
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 "/{
|
|
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.
|
|
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
|
|
46
|
-
|
|
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
|
-
"/{
|
|
63
|
+
"/{workload}/{environment}/{stack_type}/{resource_name}/{attribute}",
|
|
62
64
|
)
|
|
63
65
|
|
|
64
66
|
|
|
@@ -84,15 +86,18 @@ class EnhancedSsmConfig:
|
|
|
84
86
|
self, attribute: str, custom_path: Optional[str] = None
|
|
85
87
|
) -> str:
|
|
86
88
|
"""Generate SSM parameter path using pattern or custom path"""
|
|
87
|
-
|
|
89
|
+
# Handle custom_path - must be a string starting with "/"
|
|
90
|
+
# Protect against incorrect config like: "exports": {"enabled": true}
|
|
91
|
+
if custom_path and isinstance(custom_path, str) and custom_path.startswith("/"):
|
|
88
92
|
return custom_path
|
|
89
93
|
|
|
90
94
|
# Convert underscore attribute names to hyphen format for consistent SSM paths
|
|
91
95
|
formatted_attribute = attribute.replace("_", "-")
|
|
92
96
|
|
|
93
|
-
# Use enhanced pattern
|
|
97
|
+
# Use enhanced pattern (support both workload and organization for backward compatibility)
|
|
94
98
|
return self.pattern.format(
|
|
95
|
-
|
|
99
|
+
workload=self.workload,
|
|
100
|
+
organization=self.workload, # Backward compatibility
|
|
96
101
|
environment=self.environment,
|
|
97
102
|
stack_type=self.resource_type,
|
|
98
103
|
resource_name=self.resource_name,
|
|
@@ -236,7 +241,7 @@ class EnhancedSsmConfig:
|
|
|
236
241
|
formatted_resource_name = source_resource_name.replace("_", "-")
|
|
237
242
|
formatted_resource_type = source_resource_type.replace("_", "-")
|
|
238
243
|
|
|
239
|
-
return f"/{self.
|
|
244
|
+
return f"/{self.workload}/{self.environment}/{formatted_resource_type}/{formatted_resource_name}/{formatted_attribute}"
|
|
240
245
|
|
|
241
246
|
|
|
242
247
|
# Resource type definitions for auto-discovery
|
|
@@ -304,9 +304,28 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
|
|
|
304
304
|
)
|
|
305
305
|
|
|
306
306
|
def _setup_cognito_authorizer(self, api_gateway, api_id):
|
|
307
|
-
"""Setup Cognito authorizer if configured"""
|
|
307
|
+
"""Setup Cognito authorizer if configured AND if any routes need it"""
|
|
308
308
|
if not self.api_config.cognito_authorizer:
|
|
309
309
|
return None
|
|
310
|
+
|
|
311
|
+
# Check if any routes actually need the authorizer
|
|
312
|
+
# Don't create it if all routes are public (authorization_type: NONE)
|
|
313
|
+
routes = self.api_config.routes or []
|
|
314
|
+
needs_authorizer = any(
|
|
315
|
+
route.get("authorization_type") != "NONE"
|
|
316
|
+
for route in routes
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
# If we're not creating an authorizer but Cognito is configured,
|
|
320
|
+
# inform the integration utility so it can still perform security validations
|
|
321
|
+
if not needs_authorizer:
|
|
322
|
+
logger.info(
|
|
323
|
+
"Cognito authorizer configured but no routes require authorization. "
|
|
324
|
+
"Skipping authorizer creation but maintaining security validation context."
|
|
325
|
+
)
|
|
326
|
+
# Set a flag so the integration utility knows Cognito was available
|
|
327
|
+
self.integration_utility.cognito_configured = True
|
|
328
|
+
return None
|
|
310
329
|
|
|
311
330
|
route_config = ApiGatewayConfigRouteConfig({})
|
|
312
331
|
return self.integration_utility.get_or_create_authorizer(
|
|
@@ -316,10 +335,112 @@ class ApiGatewayStack(IStack, EnhancedSsmParameterMixin):
|
|
|
316
335
|
def _setup_lambda_routes(self, api_gateway, api_id, routes, authorizer):
|
|
317
336
|
"""Setup Lambda routes and integrations"""
|
|
318
337
|
for route in routes:
|
|
319
|
-
|
|
338
|
+
# Check if this route references an existing Lambda via SSM
|
|
339
|
+
lambda_arn_ssm_path = route.get("lambda_arn_ssm_path")
|
|
340
|
+
lambda_name_ref = route.get("lambda_name")
|
|
341
|
+
|
|
342
|
+
if lambda_arn_ssm_path or lambda_name_ref:
|
|
343
|
+
# Import existing Lambda from SSM
|
|
344
|
+
self._setup_existing_lambda_route(api_gateway, api_id, route, authorizer)
|
|
345
|
+
else:
|
|
346
|
+
# Create new Lambda (legacy pattern)
|
|
347
|
+
self._setup_single_lambda_route(api_gateway, api_id, route, authorizer)
|
|
348
|
+
|
|
349
|
+
def _setup_existing_lambda_route(self, api_gateway, api_id, route, authorizer):
|
|
350
|
+
"""
|
|
351
|
+
Setup API Gateway route with existing Lambda function imported from SSM.
|
|
352
|
+
This is the NEW PATTERN for separating Lambda and API Gateway stacks.
|
|
353
|
+
"""
|
|
354
|
+
route_path = route["path"]
|
|
355
|
+
suffix = route_path.strip("/").replace("/", "-") or "health"
|
|
356
|
+
|
|
357
|
+
# Get Lambda ARN from SSM Parameter Store
|
|
358
|
+
lambda_arn = self._get_lambda_arn_from_ssm(route)
|
|
359
|
+
|
|
360
|
+
if not lambda_arn:
|
|
361
|
+
raise ValueError(
|
|
362
|
+
f"Could not resolve Lambda ARN for route {route_path}. "
|
|
363
|
+
f"Ensure Lambda stack has deployed and exported ARN to SSM."
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
# Import Lambda function from ARN
|
|
367
|
+
lambda_fn = _lambda.Function.from_function_arn(
|
|
368
|
+
self,
|
|
369
|
+
f"{api_id}-imported-lambda-{suffix}",
|
|
370
|
+
lambda_arn
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
logger.info(f"Imported Lambda for route {route_path}: {lambda_arn}")
|
|
374
|
+
|
|
375
|
+
# Setup API Gateway resource
|
|
376
|
+
resource = (
|
|
377
|
+
api_gateway.root.resource_for_path(route_path)
|
|
378
|
+
if route_path != "/"
|
|
379
|
+
else api_gateway.root
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# Setup Lambda integration
|
|
383
|
+
self._setup_lambda_integration(
|
|
384
|
+
api_gateway, api_id, route, lambda_fn, authorizer, suffix
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# Setup CORS using centralized utility
|
|
388
|
+
self.integration_utility.setup_route_cors(resource, route_path, route)
|
|
389
|
+
|
|
390
|
+
def _get_lambda_arn_from_ssm(self, route: dict) -> str:
|
|
391
|
+
"""
|
|
392
|
+
Get Lambda ARN from SSM Parameter Store.
|
|
393
|
+
Supports both explicit SSM paths and auto-discovery via lambda_name.
|
|
394
|
+
"""
|
|
395
|
+
# Option 1: Explicit SSM path provided
|
|
396
|
+
lambda_arn_ssm_path = route.get("lambda_arn_ssm_path")
|
|
397
|
+
if lambda_arn_ssm_path:
|
|
398
|
+
logger.info(f"Looking up Lambda ARN from SSM: {lambda_arn_ssm_path}")
|
|
399
|
+
try:
|
|
400
|
+
param = ssm.StringParameter.from_string_parameter_name(
|
|
401
|
+
self,
|
|
402
|
+
f"lambda-arn-param-{hash(lambda_arn_ssm_path) % 10000}",
|
|
403
|
+
lambda_arn_ssm_path
|
|
404
|
+
)
|
|
405
|
+
return param.string_value
|
|
406
|
+
except Exception as e:
|
|
407
|
+
logger.error(f"Failed to retrieve Lambda ARN from SSM path {lambda_arn_ssm_path}: {e}")
|
|
408
|
+
raise
|
|
409
|
+
|
|
410
|
+
# Option 2: Auto-discovery via lambda_name
|
|
411
|
+
lambda_name = route.get("lambda_name")
|
|
412
|
+
if lambda_name:
|
|
413
|
+
# Build SSM path using convention from lambda_stack
|
|
414
|
+
ssm_imports_config = self.stack_config.dictionary.get("api_gateway", {}).get("ssm", {}).get("imports", {})
|
|
415
|
+
# Try 'workload' first, fall back to 'organization' for backward compatibility
|
|
416
|
+
workload = ssm_imports_config.get("workload", ssm_imports_config.get("organization", self.deployment.workload_name))
|
|
417
|
+
environment = ssm_imports_config.get("environment", self.deployment.environment)
|
|
418
|
+
|
|
419
|
+
ssm_path = f"/{workload}/{environment}/lambda/{lambda_name}/arn"
|
|
420
|
+
logger.info(f"Auto-discovering Lambda ARN from SSM: {ssm_path}")
|
|
421
|
+
|
|
422
|
+
try:
|
|
423
|
+
param = ssm.StringParameter.from_string_parameter_name(
|
|
424
|
+
self,
|
|
425
|
+
f"lambda-arn-{lambda_name}-param",
|
|
426
|
+
ssm_path
|
|
427
|
+
)
|
|
428
|
+
return param.string_value
|
|
429
|
+
except Exception as e:
|
|
430
|
+
logger.error(f"Failed to auto-discover Lambda ARN for '{lambda_name}' from {ssm_path}: {e}")
|
|
431
|
+
raise ValueError(
|
|
432
|
+
f"Lambda ARN not found in SSM for '{lambda_name}'. "
|
|
433
|
+
f"Ensure the Lambda stack has deployed and exported the ARN to: {ssm_path}"
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
return None
|
|
320
437
|
|
|
321
438
|
def _setup_single_lambda_route(self, api_gateway, api_id, route, authorizer):
|
|
322
|
-
"""
|
|
439
|
+
"""
|
|
440
|
+
Setup a single Lambda route with integration and CORS.
|
|
441
|
+
LEGACY PATTERN: Creates a new Lambda function inline.
|
|
442
|
+
Prefer _setup_existing_lambda_route for new implementations.
|
|
443
|
+
"""
|
|
323
444
|
suffix = route["path"].strip("/").replace("/", "-") or "health"
|
|
324
445
|
src = route.get("src")
|
|
325
446
|
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
|
|
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.
|
|
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
|
-
#
|
|
94
|
-
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
|
-
#
|
|
110
|
-
self.
|
|
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
|
-
#
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
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
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
if not
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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
|
-
#
|
|
342
|
-
|
|
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
|
-
#
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
)
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
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
|
|
@@ -40,6 +40,7 @@ class ApiGatewayIntegrationUtility:
|
|
|
40
40
|
self.account = scope.account
|
|
41
41
|
self.api_gateway = None
|
|
42
42
|
self.authorizer = None
|
|
43
|
+
self.cognito_configured = False # Flag for when Cognito is configured but authorizer not created
|
|
43
44
|
self._log_group = None
|
|
44
45
|
self._log_role = None
|
|
45
46
|
|
|
@@ -55,8 +56,10 @@ class ApiGatewayIntegrationUtility:
|
|
|
55
56
|
raise ValueError("API Gateway config is missing in Lambda function config")
|
|
56
57
|
|
|
57
58
|
# Validate authorization configuration for security
|
|
59
|
+
# Check if Cognito is available (either authorizer created OR configured but not created)
|
|
58
60
|
has_cognito_authorizer = (
|
|
59
61
|
self.authorizer is not None
|
|
62
|
+
or self.cognito_configured
|
|
60
63
|
or self._get_existing_authorizer_id_with_ssm_fallback(
|
|
61
64
|
api_config, stack_config
|
|
62
65
|
)
|
|
@@ -614,6 +617,10 @@ class ApiGatewayIntegrationUtility:
|
|
|
614
617
|
authorizer_name=authorizer_name,
|
|
615
618
|
identity_source=identity_source,
|
|
616
619
|
)
|
|
620
|
+
|
|
621
|
+
# The authorizer is automatically attached to the API Gateway when used in a method
|
|
622
|
+
# But we need to ensure it's created in the context of the API's scope
|
|
623
|
+
# The actual attachment happens when the authorizer is referenced in method creation
|
|
617
624
|
|
|
618
625
|
return self.authorizer
|
|
619
626
|
|
cdk_factory/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.
|
|
1
|
+
__version__ = "0.8.1"
|
|
@@ -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=
|
|
4
|
+
cdk_factory/version.py,sha256=Ocl79hbbH8_jdr5dGC90VR1cAvZc05Rc0tkZttUnMjo,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=
|
|
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=
|
|
13
|
-
cdk_factory/configurations/enhanced_ssm_config.py,sha256=
|
|
12
|
+
cdk_factory/configurations/enhanced_base_config.py,sha256=Y1gcGZxyf_O2KFnVpCBORSGHWHqHxw0vNx_ijMad_QA,6654
|
|
13
|
+
cdk_factory/configurations/enhanced_ssm_config.py,sha256=VDmIUSrj8nYS6fE9DdOp5yi9jUfMfitz5A7kCuPS-kI,12895
|
|
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
|
|
@@ -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=
|
|
76
|
+
cdk_factory/stack_library/api_gateway/api_gateway_stack.py,sha256=5rWHwLc6kI99YHUA1xqLtzcEhDmMB4p-xB9gLQ6o8JE,37916
|
|
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=
|
|
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
|
|
@@ -100,7 +100,7 @@ cdk_factory/stack_library/vpc/__init__.py,sha256=7pIqP97Gf2AJbv9Ebp1WbQGHYhgEbWJ
|
|
|
100
100
|
cdk_factory/stack_library/vpc/vpc_stack.py,sha256=zdDiGilf03esxuya5Z8zVYSVMAIuZBeD-ZKgfnEd6aw,10077
|
|
101
101
|
cdk_factory/stack_library/websites/static_website_stack.py,sha256=KBQiV6PI09mpHGtH-So5Hk3uhfFLDepoXInGbfin0cY,7938
|
|
102
102
|
cdk_factory/stages/websites/static_website_stage.py,sha256=X4fpKXkhb0zIbSHx3QyddBhVSLBryb1vf1Cg2fMTqog,755
|
|
103
|
-
cdk_factory/utilities/api_gateway_integration_utility.py,sha256=
|
|
103
|
+
cdk_factory/utilities/api_gateway_integration_utility.py,sha256=yblKiMIHGXqKb7JK5IbzGM_TXjX9j893BMqgqBT44DE,63449
|
|
104
104
|
cdk_factory/utilities/commandline_args.py,sha256=0FiNEJFbWVN8Ct7r0VHnJEx7rhUlaRKT7R7HMNJBSTI,2216
|
|
105
105
|
cdk_factory/utilities/configuration_loader.py,sha256=z0ZdGLNbTO4_yfluB9zUh_i_Poc9qj-7oRyjMRlNkN8,1522
|
|
106
106
|
cdk_factory/utilities/docker_utilities.py,sha256=9r8C-lXYpymqEfi3gTeWCQzHldvfjttPqn6p3j2khTE,8111
|
|
@@ -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.
|
|
116
|
-
cdk_factory-0.
|
|
117
|
-
cdk_factory-0.
|
|
118
|
-
cdk_factory-0.
|
|
115
|
+
cdk_factory-0.8.1.dist-info/METADATA,sha256=QzIZhYxS_Mu7hgXTGdT4yN4K0gtJP6Z1Cn1C7onszAg,2450
|
|
116
|
+
cdk_factory-0.8.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
117
|
+
cdk_factory-0.8.1.dist-info/licenses/LICENSE,sha256=NOtdOeLwg2il_XBJdXUPFPX8JlV4dqTdDGAd2-khxT8,1066
|
|
118
|
+
cdk_factory-0.8.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|