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.
- cdk_factory/configurations/base_config.py +23 -24
- cdk_factory/configurations/cdk_config.py +1 -1
- cdk_factory/configurations/deployment.py +12 -0
- cdk_factory/configurations/devops.py +1 -1
- cdk_factory/configurations/resources/acm.py +9 -2
- cdk_factory/configurations/resources/auto_scaling.py +7 -5
- cdk_factory/configurations/resources/cloudfront.py +7 -2
- cdk_factory/configurations/resources/ecr.py +1 -1
- cdk_factory/configurations/resources/ecs_cluster.py +12 -5
- cdk_factory/configurations/resources/ecs_service.py +30 -3
- cdk_factory/configurations/resources/lambda_edge.py +18 -4
- cdk_factory/configurations/resources/load_balancer.py +8 -9
- cdk_factory/configurations/resources/monitoring.py +8 -3
- cdk_factory/configurations/resources/rds.py +8 -9
- cdk_factory/configurations/resources/route53.py +5 -0
- cdk_factory/configurations/resources/rum.py +7 -2
- cdk_factory/configurations/resources/s3.py +10 -2
- cdk_factory/configurations/resources/security_group_full_stack.py +7 -8
- cdk_factory/configurations/resources/vpc.py +19 -0
- cdk_factory/configurations/workload.py +32 -2
- cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py +1 -1
- cdk_factory/constructs/ecr/ecr_construct.py +9 -2
- cdk_factory/constructs/lambdas/policies/policy_docs.py +4 -4
- cdk_factory/interfaces/istack.py +4 -4
- cdk_factory/interfaces/networked_stack_mixin.py +6 -6
- cdk_factory/interfaces/standardized_ssm_mixin.py +684 -0
- cdk_factory/interfaces/vpc_provider_mixin.py +64 -33
- cdk_factory/lambdas/edge/ip_gate/handler.py +42 -40
- cdk_factory/pipeline/pipeline_factory.py +3 -3
- cdk_factory/stack_library/__init__.py +3 -2
- cdk_factory/stack_library/acm/acm_stack.py +7 -17
- cdk_factory/stack_library/api_gateway/api_gateway_stack.py +84 -59
- cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +454 -537
- cdk_factory/stack_library/cloudfront/cloudfront_stack.py +76 -22
- cdk_factory/stack_library/code_artifact/code_artifact_stack.py +5 -27
- cdk_factory/stack_library/cognito/cognito_stack.py +152 -92
- cdk_factory/stack_library/dynamodb/dynamodb_stack.py +19 -15
- cdk_factory/stack_library/ecr/ecr_stack.py +2 -2
- cdk_factory/stack_library/ecs/__init__.py +1 -3
- cdk_factory/stack_library/ecs/ecs_cluster_stack.py +159 -75
- cdk_factory/stack_library/ecs/ecs_service_stack.py +59 -52
- cdk_factory/stack_library/lambda_edge/EDGE_LOG_RETENTION_TODO.md +226 -0
- cdk_factory/stack_library/lambda_edge/LAMBDA_EDGE_LOG_RETENTION_BLOG.md +215 -0
- cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +240 -83
- cdk_factory/stack_library/load_balancer/load_balancer_stack.py +139 -212
- cdk_factory/stack_library/rds/rds_stack.py +74 -98
- cdk_factory/stack_library/route53/route53_stack.py +246 -40
- cdk_factory/stack_library/rum/rum_stack.py +108 -91
- cdk_factory/stack_library/security_group/security_group_full_stack.py +10 -53
- cdk_factory/stack_library/security_group/security_group_stack.py +12 -19
- cdk_factory/stack_library/simple_queue_service/sqs_stack.py +1 -34
- cdk_factory/stack_library/stack_base.py +5 -0
- cdk_factory/stack_library/vpc/vpc_stack.py +171 -130
- cdk_factory/stack_library/websites/static_website_stack.py +7 -3
- cdk_factory/utilities/api_gateway_integration_utility.py +24 -16
- cdk_factory/utilities/environment_services.py +5 -5
- cdk_factory/utilities/json_loading_utility.py +1 -1
- cdk_factory/validation/config_validator.py +483 -0
- cdk_factory/version.py +1 -1
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/METADATA +1 -1
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/RECORD +64 -62
- cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -321
- cdk_factory/interfaces/ssm_parameter_mixin.py +0 -454
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/WHEEL +0 -0
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/entry_points.txt +0 -0
- {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -25,7 +25,7 @@ from cdk_factory.configurations.deployment import DeploymentConfig
|
|
|
25
25
|
from cdk_factory.configurations.stack import StackConfig
|
|
26
26
|
from cdk_factory.configurations.resources.lambda_edge import LambdaEdgeConfig
|
|
27
27
|
from cdk_factory.interfaces.istack import IStack
|
|
28
|
-
from cdk_factory.interfaces.
|
|
28
|
+
from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
|
|
29
29
|
from cdk_factory.stack.stack_module_registry import register_stack
|
|
30
30
|
from cdk_factory.workload.workload_factory import WorkloadConfig
|
|
31
31
|
|
|
@@ -34,7 +34,7 @@ logger = Logger(service="LambdaEdgeStack")
|
|
|
34
34
|
|
|
35
35
|
@register_stack("lambda_edge_library_module")
|
|
36
36
|
@register_stack("lambda_edge_stack")
|
|
37
|
-
class LambdaEdgeStack(IStack,
|
|
37
|
+
class LambdaEdgeStack(IStack, StandardizedSsmMixin):
|
|
38
38
|
"""
|
|
39
39
|
Reusable stack for Lambda@Edge functions.
|
|
40
40
|
|
|
@@ -53,6 +53,8 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
53
53
|
self.workload: Optional[WorkloadConfig] = None
|
|
54
54
|
self.function: Optional[_lambda.Function] = None
|
|
55
55
|
self.function_version: Optional[_lambda.Version] = None
|
|
56
|
+
# Cache for resolved environment variables to prevent duplicate construct creation
|
|
57
|
+
self._resolved_env_cache: Optional[Dict[str, str]] = None
|
|
56
58
|
|
|
57
59
|
def build(
|
|
58
60
|
self,
|
|
@@ -98,41 +100,74 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
98
100
|
# Create version (required for Lambda@Edge)
|
|
99
101
|
self._create_function_version(function_name)
|
|
100
102
|
|
|
103
|
+
# Configure edge log retention for regional logs
|
|
104
|
+
self._configure_edge_log_retention(function_name)
|
|
105
|
+
|
|
101
106
|
# Add outputs
|
|
102
107
|
self._add_outputs(function_name)
|
|
103
108
|
|
|
109
|
+
def _sanitize_construct_name(self, name: str) -> str:
|
|
110
|
+
"""
|
|
111
|
+
Create a deterministic, valid CDK construct name from any string.
|
|
112
|
+
Replaces non-alphanumeric characters with dashes and limits length.
|
|
113
|
+
"""
|
|
114
|
+
# Replace non-alphanumeric characters with dashes
|
|
115
|
+
sanitized = ''.join(c if c.isalnum() else '-' for c in name)
|
|
116
|
+
# Remove consecutive dashes
|
|
117
|
+
while '--' in sanitized:
|
|
118
|
+
sanitized = sanitized.replace('--', '-')
|
|
119
|
+
# Remove leading/trailing dashes
|
|
120
|
+
sanitized = sanitized.strip('-')
|
|
121
|
+
# Limit to 255 characters (CDK limit)
|
|
122
|
+
return sanitized[:255]
|
|
123
|
+
|
|
104
124
|
def _resolve_environment_variables(self) -> Dict[str, str]:
|
|
105
125
|
"""
|
|
106
126
|
Resolve environment variables, including SSM parameter references.
|
|
107
127
|
Supports {{ssm:parameter-path}} syntax for dynamic SSM lookups.
|
|
108
128
|
Uses CDK tokens that resolve at deployment time, not synthesis time.
|
|
129
|
+
Caches results to prevent duplicate construct creation.
|
|
109
130
|
"""
|
|
131
|
+
# Return cached result if available
|
|
132
|
+
if self._resolved_env_cache is not None:
|
|
133
|
+
return self._resolved_env_cache
|
|
134
|
+
|
|
110
135
|
resolved_env = {}
|
|
111
136
|
|
|
112
|
-
|
|
137
|
+
# Use the new simplified configuration structure
|
|
138
|
+
configuration = self.edge_config.dictionary.get("configuration", {})
|
|
139
|
+
runtime_config = configuration.get("runtime", {})
|
|
140
|
+
ui_config = configuration.get("ui", {})
|
|
141
|
+
|
|
142
|
+
for key, value in runtime_config.items():
|
|
113
143
|
# Check if value is an SSM parameter reference
|
|
114
144
|
if isinstance(value, str) and value.startswith("{{ssm:") and value.endswith("}}"):
|
|
115
145
|
# Extract SSM parameter path
|
|
116
146
|
ssm_param_path = value[6:-2] # Remove {{ssm: and }}
|
|
117
147
|
|
|
148
|
+
# Create deterministic construct name from parameter path
|
|
149
|
+
construct_name = self._sanitize_construct_name(f"env-{key}-{ssm_param_path}")
|
|
150
|
+
|
|
118
151
|
# Import SSM parameter - this creates a token that resolves at deployment time
|
|
119
152
|
param = ssm.StringParameter.from_string_parameter_name(
|
|
120
153
|
self,
|
|
121
|
-
|
|
154
|
+
construct_name,
|
|
122
155
|
ssm_param_path
|
|
123
156
|
)
|
|
124
157
|
resolved_value = param.string_value
|
|
125
|
-
logger.info(f"Resolved environment variable {key} from SSM {ssm_param_path}")
|
|
158
|
+
logger.info(f"Resolved environment variable {key} from SSM {ssm_param_path} as {construct_name}")
|
|
126
159
|
resolved_env[key] = resolved_value
|
|
127
160
|
else:
|
|
128
161
|
resolved_env[key] = value
|
|
129
162
|
|
|
163
|
+
# Cache the result
|
|
164
|
+
self._resolved_env_cache = resolved_env
|
|
130
165
|
return resolved_env
|
|
131
166
|
|
|
132
167
|
def _create_lambda_function(self, function_name: str) -> None:
|
|
133
168
|
"""Create the Lambda function"""
|
|
134
169
|
|
|
135
|
-
# Resolve code path - support package references (e.g., "cdk_factory:lambdas/
|
|
170
|
+
# Resolve code path - support package references (e.g., "cdk_factory:lambdas/cloudfront/ip_gate")
|
|
136
171
|
code_path_str = self.edge_config.code_path
|
|
137
172
|
|
|
138
173
|
if ':' in code_path_str:
|
|
@@ -185,10 +220,19 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
185
220
|
# Create runtime configuration file for Lambda@Edge
|
|
186
221
|
# Since Lambda@Edge doesn't support environment variables, we bundle a config file
|
|
187
222
|
# Use the full function_name (e.g., "tech-talk-dev-ip-gate") not just the base name
|
|
223
|
+
resolved_env = self._resolve_environment_variables()
|
|
224
|
+
|
|
225
|
+
# Get the UI configuration
|
|
226
|
+
configuration = self.edge_config.dictionary.get("configuration", {})
|
|
227
|
+
ui_config = configuration.get("ui", {})
|
|
228
|
+
|
|
188
229
|
runtime_config = {
|
|
189
230
|
'environment': self.deployment.environment,
|
|
231
|
+
'workload': self.deployment.workload,
|
|
190
232
|
'function_name': function_name,
|
|
191
|
-
'region': self.deployment.region
|
|
233
|
+
'region': self.deployment.region,
|
|
234
|
+
'runtime': resolved_env, # Runtime variables (SSM, etc.)
|
|
235
|
+
'ui': ui_config # UI configuration (colors, messages, etc.)
|
|
192
236
|
}
|
|
193
237
|
|
|
194
238
|
runtime_config_path = temp_code_dir / 'runtime_config.json'
|
|
@@ -216,21 +260,17 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
216
260
|
self.edge_config.runtime,
|
|
217
261
|
_lambda.Runtime.PYTHON_3_11
|
|
218
262
|
)
|
|
219
|
-
|
|
220
|
-
# Lambda@Edge does NOT support environment variables
|
|
221
|
-
# Configuration must be handled via:
|
|
222
|
-
# 1. Hardcoded in the function code
|
|
223
|
-
# 2. Fetched from SSM Parameter Store at runtime
|
|
224
|
-
# 3. Other configuration mechanisms
|
|
225
|
-
|
|
263
|
+
|
|
226
264
|
# Log warning if environment variables are configured
|
|
227
|
-
|
|
265
|
+
configuration = self.edge_config.dictionary.get("configuration", {})
|
|
266
|
+
runtime_config = configuration.get("runtime", {})
|
|
267
|
+
|
|
268
|
+
if runtime_config:
|
|
228
269
|
logger.warning(
|
|
229
270
|
f"Lambda@Edge function '{function_name}' has environment variables configured, "
|
|
230
|
-
"but Lambda@Edge does not support environment variables. "
|
|
231
|
-
"The function must fetch these values from SSM Parameter Store at runtime."
|
|
271
|
+
"but Lambda@Edge does not support environment variables. The function must fetch these values from SSM Parameter Store at runtime."
|
|
232
272
|
)
|
|
233
|
-
for key, value in
|
|
273
|
+
for key, value in runtime_config.items():
|
|
234
274
|
logger.warning(f" - {key}: {value}")
|
|
235
275
|
|
|
236
276
|
# Create execution role with CloudWatch Logs and SSM permissions
|
|
@@ -239,7 +279,8 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
239
279
|
f"{function_name}-Role",
|
|
240
280
|
assumed_by=iam.CompositePrincipal(
|
|
241
281
|
iam.ServicePrincipal("lambda.amazonaws.com"),
|
|
242
|
-
iam.ServicePrincipal("edgelambda.amazonaws.com")
|
|
282
|
+
iam.ServicePrincipal("edgelambda.amazonaws.com"),
|
|
283
|
+
iam.ServicePrincipal("cloudfront.amazonaws.com") # Add CloudFront service principal
|
|
243
284
|
),
|
|
244
285
|
description=f"Execution role for Lambda@Edge function {function_name}",
|
|
245
286
|
managed_policies=[
|
|
@@ -250,7 +291,7 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
250
291
|
)
|
|
251
292
|
|
|
252
293
|
# Add SSM read permissions if environment variables reference SSM parameters
|
|
253
|
-
if
|
|
294
|
+
if runtime_config:
|
|
254
295
|
execution_role.add_to_policy(
|
|
255
296
|
iam.PolicyStatement(
|
|
256
297
|
effect=iam.Effect.ALLOW,
|
|
@@ -260,12 +301,90 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
260
301
|
"ssm:GetParametersByPath"
|
|
261
302
|
],
|
|
262
303
|
resources=[
|
|
263
|
-
f"arn:aws:ssm:*:{
|
|
304
|
+
f"arn:aws:ssm:*:{self.deployment.account}:parameter/*"
|
|
264
305
|
]
|
|
265
306
|
)
|
|
266
307
|
)
|
|
267
308
|
|
|
268
|
-
#
|
|
309
|
+
# Add Secrets Manager permissions for origin secret access
|
|
310
|
+
execution_role.add_to_policy(
|
|
311
|
+
iam.PolicyStatement(
|
|
312
|
+
effect=iam.Effect.ALLOW,
|
|
313
|
+
actions=[
|
|
314
|
+
"secretsmanager:GetSecretValue",
|
|
315
|
+
"secretsmanager:DescribeSecret"
|
|
316
|
+
],
|
|
317
|
+
resources=[
|
|
318
|
+
f"arn:aws:secretsmanager:*:{self.deployment.account}:secret:{self.deployment.environment}/{self.workload.name}/origin-secret*"
|
|
319
|
+
]
|
|
320
|
+
)
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
# Add ELB permissions for target health API access
|
|
324
|
+
execution_role.add_to_policy(
|
|
325
|
+
iam.PolicyStatement(
|
|
326
|
+
effect=iam.Effect.ALLOW,
|
|
327
|
+
actions=[
|
|
328
|
+
"elasticloadbalancing:DescribeTargetHealth",
|
|
329
|
+
"elasticloadbalancing:DescribeTargetGroups",
|
|
330
|
+
"elasticloadbalancing:DescribeLoadBalancers",
|
|
331
|
+
"elasticloadbalancing:DescribeListeners",
|
|
332
|
+
"elasticloadbalancing:DescribeTags"
|
|
333
|
+
],
|
|
334
|
+
resources=[
|
|
335
|
+
f"arn:aws:elasticloadbalancing:*:{self.deployment.account}:targetgroup/*/*",
|
|
336
|
+
f"arn:aws:elasticloadbalancing:*:{self.deployment.account}:loadbalancer/*/*",
|
|
337
|
+
f"arn:aws:elasticloadbalancing:*:{self.deployment.account}:listener/*/*/*/*"
|
|
338
|
+
]
|
|
339
|
+
)
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
# Add ACM permissions for certificate validation
|
|
343
|
+
execution_role.add_to_policy(
|
|
344
|
+
iam.PolicyStatement(
|
|
345
|
+
effect=iam.Effect.ALLOW,
|
|
346
|
+
actions=[
|
|
347
|
+
"acm:DescribeCertificate",
|
|
348
|
+
"acm:ListCertificates"
|
|
349
|
+
],
|
|
350
|
+
resources=[
|
|
351
|
+
f"arn:aws:acm:*:{self.deployment.account}:certificate/*"
|
|
352
|
+
]
|
|
353
|
+
)
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
# Add Route 53 permissions for health check access
|
|
357
|
+
execution_role.add_to_policy(
|
|
358
|
+
iam.PolicyStatement(
|
|
359
|
+
effect=iam.Effect.ALLOW,
|
|
360
|
+
actions=[
|
|
361
|
+
"route53:GetHealthCheckStatus",
|
|
362
|
+
"route53:ListHealthChecks",
|
|
363
|
+
"route53:GetHealthCheck"
|
|
364
|
+
],
|
|
365
|
+
resources=[
|
|
366
|
+
f"arn:aws:route53:::{self.deployment.account}:health-check/*"
|
|
367
|
+
]
|
|
368
|
+
)
|
|
369
|
+
)
|
|
370
|
+
|
|
371
|
+
# Add CloudWatch permissions for enhanced logging and metrics
|
|
372
|
+
execution_role.add_to_policy(
|
|
373
|
+
iam.PolicyStatement(
|
|
374
|
+
effect=iam.Effect.ALLOW,
|
|
375
|
+
actions=[
|
|
376
|
+
"logs:CreateLogGroup",
|
|
377
|
+
"logs:CreateLogStream",
|
|
378
|
+
"logs:PutLogEvents",
|
|
379
|
+
"cloudwatch:PutMetricData"
|
|
380
|
+
],
|
|
381
|
+
resources=[
|
|
382
|
+
f"arn:aws:logs:*:{self.deployment.account}:log-group:/aws/lambda/*",
|
|
383
|
+
f"arn:aws:cloudwatch:*:{self.deployment.account}:metric:*"
|
|
384
|
+
]
|
|
385
|
+
)
|
|
386
|
+
)
|
|
387
|
+
|
|
269
388
|
self.function = _lambda.Function(
|
|
270
389
|
self,
|
|
271
390
|
function_name,
|
|
@@ -278,6 +397,7 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
278
397
|
description=self.edge_config.description,
|
|
279
398
|
role=execution_role,
|
|
280
399
|
# Lambda@Edge does NOT support environment variables
|
|
400
|
+
# Configuration must be fetched from SSM at runtime
|
|
281
401
|
log_retention=logs.RetentionDays.ONE_WEEK,
|
|
282
402
|
)
|
|
283
403
|
|
|
@@ -285,6 +405,36 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
285
405
|
for key, value in self.edge_config.tags.items():
|
|
286
406
|
cdk.Tags.of(self.function).add(key, value)
|
|
287
407
|
|
|
408
|
+
# Add resource-based policy allowing CloudFront to invoke the Lambda function
|
|
409
|
+
# This is REQUIRED for Lambda@Edge to work properly
|
|
410
|
+
permission_kwargs = {
|
|
411
|
+
"principal": iam.ServicePrincipal("cloudfront.amazonaws.com"),
|
|
412
|
+
"action": "lambda:InvokeFunction",
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
# Optional: Add source ARN restriction if CloudFront distribution ARN is available
|
|
416
|
+
# This provides more secure permission scoping
|
|
417
|
+
distribution_arn_path = f"/{self.deployment.environment}/{self.workload.name}/cloudfront/arn"
|
|
418
|
+
try:
|
|
419
|
+
distribution_arn = ssm.StringParameter.from_string_parameter_name(
|
|
420
|
+
self,
|
|
421
|
+
"cloudfront-distribution-arn",
|
|
422
|
+
distribution_arn_path
|
|
423
|
+
).string_value
|
|
424
|
+
|
|
425
|
+
# Add source ARN condition for more secure permission scoping
|
|
426
|
+
permission_kwargs["source_arn"] = distribution_arn
|
|
427
|
+
logger.info(f"Adding CloudFront permission with source ARN restriction: {distribution_arn}")
|
|
428
|
+
except Exception:
|
|
429
|
+
# Distribution ARN not available (common during initial deployment)
|
|
430
|
+
# CloudFront will scope the permission appropriately when it associates the Lambda
|
|
431
|
+
logger.warning(f"CloudFront distribution ARN not found at {distribution_arn_path}, using open permission")
|
|
432
|
+
|
|
433
|
+
self.function.add_permission(
|
|
434
|
+
"CloudFrontInvokePermission",
|
|
435
|
+
**permission_kwargs
|
|
436
|
+
)
|
|
437
|
+
|
|
288
438
|
def _create_function_version(self, function_name: str) -> None:
|
|
289
439
|
"""
|
|
290
440
|
Create a version of the Lambda function.
|
|
@@ -300,36 +450,49 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
300
450
|
f"Version for Lambda@Edge deployment - {self.edge_config.description}"
|
|
301
451
|
)
|
|
302
452
|
|
|
303
|
-
def
|
|
304
|
-
"""
|
|
453
|
+
def _configure_edge_log_retention(self, function_name: str) -> None:
|
|
454
|
+
"""
|
|
455
|
+
Configure log retention for Lambda@Edge log groups in all edge regions
|
|
305
456
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
description="Lambda function name",
|
|
312
|
-
export_name=f"{function_name}-name"
|
|
313
|
-
)
|
|
457
|
+
TODO: IMPLEMENT POST-DEPLOYMENT SOLUTION
|
|
458
|
+
--------------------------------------
|
|
459
|
+
Lambda@Edge log groups are created on-demand when the function is invoked
|
|
460
|
+
at edge locations, not during deployment. This means we cannot set retention
|
|
461
|
+
policies during CloudFormation deployment.
|
|
314
462
|
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
export_name=f"{function_name}-arn"
|
|
321
|
-
)
|
|
463
|
+
Possible solutions to implement:
|
|
464
|
+
1. EventBridge rule that triggers on log group creation
|
|
465
|
+
2. Custom Lambda function that runs periodically to set retention
|
|
466
|
+
3. Post-deployment script that waits for log groups to appear
|
|
467
|
+
4. CloudWatch Logs subscription filter that handles new log groups
|
|
322
468
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
469
|
+
Current behavior: DISABLED to prevent deployment failures
|
|
470
|
+
"""
|
|
471
|
+
|
|
472
|
+
# DISABLED: Edge log groups don't exist during deployment
|
|
473
|
+
# Lambda@Edge creates log groups on-demand at edge locations
|
|
474
|
+
# Setting retention policies during deployment fails with "log group does not exist"
|
|
475
|
+
|
|
476
|
+
edge_retention_days = self.edge_config.dictionary.get("edge_log_retention_days", 7)
|
|
477
|
+
logger.warning(
|
|
478
|
+
f"Edge log retention configuration disabled - log groups are created on-demand. "
|
|
479
|
+
f"Desired retention: {edge_retention_days} days. "
|
|
480
|
+
f"See TODO in _configure_edge_log_retention() for implementation approach."
|
|
329
481
|
)
|
|
330
482
|
|
|
483
|
+
# TODO: Implement one of these solutions:
|
|
484
|
+
# 1. EventBridge + Lambda: Trigger on log group creation and set retention
|
|
485
|
+
# 2. Periodic Lambda: Scan for edge log groups and apply retention policies
|
|
486
|
+
# 3. Post-deployment script: Wait for log groups to appear after edge replication
|
|
487
|
+
# 4. CloudWatch Logs subscription: Process new log group events
|
|
488
|
+
|
|
489
|
+
return
|
|
490
|
+
|
|
491
|
+
def _add_outputs(self, function_name: str) -> None:
|
|
492
|
+
"""Add CloudFormation outputs and SSM exports"""
|
|
493
|
+
|
|
331
494
|
# SSM Parameter Store exports (if configured)
|
|
332
|
-
ssm_exports = self.edge_config.dictionary.get("
|
|
495
|
+
ssm_exports = self.edge_config.dictionary.get("ssm", {}).get("exports", {})
|
|
333
496
|
if ssm_exports:
|
|
334
497
|
export_values = {
|
|
335
498
|
"function_name": self.function.function_name,
|
|
@@ -349,40 +512,34 @@ class LambdaEdgeStack(IStack, EnhancedSsmParameterMixin):
|
|
|
349
512
|
description=f"{key} for Lambda@Edge function {function_name}"
|
|
350
513
|
)
|
|
351
514
|
|
|
352
|
-
# Export
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
for
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
self,
|
|
384
|
-
f"env-{env_key}-param",
|
|
385
|
-
env_value,
|
|
386
|
-
ssm_path,
|
|
387
|
-
description=f"Configuration for Lambda@Edge: {env_key}"
|
|
388
|
-
)
|
|
515
|
+
# Export the complete configuration as a single SSM parameter
|
|
516
|
+
config_ssm_path = f"/{self.deployment.environment}/{self.workload.name}/lambda-edge/config"
|
|
517
|
+
configuration = self.edge_config.dictionary.get("configuration", {})
|
|
518
|
+
environment_variables = configuration.get("environment_variables", {})
|
|
519
|
+
|
|
520
|
+
# Build full configuration that Lambda@Edge expects
|
|
521
|
+
full_config = {
|
|
522
|
+
"environment_variables": environment_variables,
|
|
523
|
+
"runtime": configuration.get("runtime", {}),
|
|
524
|
+
"ui": configuration.get("ui", {})
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
self.export_ssm_parameter(
|
|
528
|
+
self,
|
|
529
|
+
"full-config-param",
|
|
530
|
+
json.dumps(full_config),
|
|
531
|
+
config_ssm_path,
|
|
532
|
+
description=f"Complete Lambda@Edge configuration for {function_name} - update this for dynamic changes"
|
|
533
|
+
)
|
|
534
|
+
|
|
535
|
+
# Export cache TTL parameter for dynamic cache control
|
|
536
|
+
cache_ttl_ssm_path = f"/{self.deployment.environment}/{self.workload.name}/lambda-edge/cache-ttl"
|
|
537
|
+
default_cache_ttl = self.edge_config.dictionary.get("cache_ttl_seconds", 300) # Default 5 minutes
|
|
538
|
+
|
|
539
|
+
self.export_ssm_parameter(
|
|
540
|
+
self,
|
|
541
|
+
"cache-ttl-param",
|
|
542
|
+
str(default_cache_ttl),
|
|
543
|
+
cache_ttl_ssm_path,
|
|
544
|
+
description=f"Lambda@Edge configuration cache TTL in seconds for {function_name} - adjust for maintenance windows (30-3600)"
|
|
545
|
+
)
|