cdk-factory 0.9.12__tar.gz → 0.10.0__tar.gz

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.
Files changed (158) hide show
  1. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/PKG-INFO +1 -1
  2. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/pyproject.toml +1 -1
  3. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/run-tests.sh +10 -6
  4. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/auto_scaling.py +27 -0
  5. cdk_factory-0.10.0/src/cdk_factory/configurations/resources/cloudfront.py +124 -0
  6. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/ecs_service.py +12 -0
  7. cdk_factory-0.10.0/src/cdk_factory/configurations/resources/lambda_edge.py +92 -0
  8. cdk_factory-0.10.0/src/cdk_factory/configurations/resources/monitoring.py +74 -0
  9. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py +51 -1
  10. cdk_factory-0.10.0/src/cdk_factory/lambdas/edge/ip_gate/handler.py +104 -0
  11. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +99 -0
  12. cdk_factory-0.10.0/src/cdk_factory/stack_library/cloudfront/__init__.py +6 -0
  13. cdk_factory-0.10.0/src/cdk_factory/stack_library/cloudfront/cloudfront_stack.py +627 -0
  14. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/ecs/ecs_service_stack.py +90 -0
  15. cdk_factory-0.10.0/src/cdk_factory/stack_library/lambda_edge/__init__.py +6 -0
  16. cdk_factory-0.10.0/src/cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +217 -0
  17. cdk_factory-0.10.0/src/cdk_factory/stack_library/monitoring/__init__.py +6 -0
  18. cdk_factory-0.10.0/src/cdk_factory/stack_library/monitoring/monitoring_stack.py +492 -0
  19. cdk_factory-0.10.0/src/cdk_factory/version.py +1 -0
  20. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/workload/workload_factory.py +2 -0
  21. cdk_factory-0.9.12/src/cdk_factory/configurations/resources/cloudfront.py +0 -34
  22. cdk_factory-0.9.12/src/cdk_factory/version.py +0 -1
  23. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/.gitignore +0 -0
  24. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/.windsurfrules +0 -0
  25. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/LICENSE +0 -0
  26. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/README.md +0 -0
  27. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/archive/README.md +0 -0
  28. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/archive/migrate_to_enhanced_ssm.py +0 -0
  29. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/examples/json-imports/README.md +0 -0
  30. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/examples/separate-api-gateway/README.md +0 -0
  31. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/examples/separate-api-gateway/api-gateway-stack.json +0 -0
  32. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/examples/separate-api-gateway/config.json +0 -0
  33. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/examples/separate-api-gateway/lambda-stack.json +0 -0
  34. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/mypy.ini +0 -0
  35. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/publish_to_pypi.py +0 -0
  36. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/publish_to_pypi.sh +0 -0
  37. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/pysetup.py +0 -0
  38. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/pysetup.sh +0 -0
  39. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/requirements.dev.txt +0 -0
  40. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/requirements.tests.txt +0 -0
  41. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/requirements.txt +0 -0
  42. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/run-checks.sh +0 -0
  43. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/run-tests-clean-venv.sh +0 -0
  44. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/__init__.py +0 -0
  45. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/app.py +0 -0
  46. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/builds/README.md +0 -0
  47. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/cdk.json +0 -0
  48. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/cli.py +0 -0
  49. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/commands/command_loader.py +0 -0
  50. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/base_config.py +0 -0
  51. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/cdk_config.py +0 -0
  52. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/deployment.py +0 -0
  53. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/deployment_wave.py +0 -0
  54. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/devops.py +0 -0
  55. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/enhanced_base_config.py +0 -0
  56. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/enhanced_ssm_config.py +0 -0
  57. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/management.py +0 -0
  58. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/pipeline.py +0 -0
  59. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/pipeline_stage.py +0 -0
  60. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/_resources.py +0 -0
  61. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/api_gateway.py +0 -0
  62. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/apigateway_route_config.py +0 -0
  63. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/cloudwatch_widget.py +0 -0
  64. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/code_artifact.py +0 -0
  65. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/code_artifact_login.py +0 -0
  66. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/code_repository.py +0 -0
  67. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/cognito.py +0 -0
  68. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/docker.py +0 -0
  69. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/dynamodb.py +0 -0
  70. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/ecr.py +0 -0
  71. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/exisiting.py +0 -0
  72. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/lambda_function.py +0 -0
  73. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/lambda_layers.py +0 -0
  74. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/lambda_triggers.py +0 -0
  75. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/load_balancer.py +0 -0
  76. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/rds.py +0 -0
  77. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/resource_mapping.py +0 -0
  78. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/resource_naming.py +0 -0
  79. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/resource_types.py +0 -0
  80. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/route53.py +0 -0
  81. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/route53_hosted_zone.py +0 -0
  82. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/rum.py +0 -0
  83. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/s3.py +0 -0
  84. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/security_group.py +0 -0
  85. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/security_group_full_stack.py +0 -0
  86. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/sqs.py +0 -0
  87. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/resources/vpc.py +0 -0
  88. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/stack.py +0 -0
  89. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/configurations/workload.py +0 -0
  90. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/ecr/ecr_construct.py +0 -0
  91. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/lambdas/lambda_function_construct.py +0 -0
  92. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/lambdas/lambda_function_docker_construct.py +0 -0
  93. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/lambdas/lambda_function_role_construct.py +0 -0
  94. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/lambdas/policies/policy_docs.py +0 -0
  95. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/lambdas/policies/policy_statements.py +0 -0
  96. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/s3_buckets/s3_bucket_construct.py +0 -0
  97. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/s3_buckets/s3_bucket_replication_destination_construct.py +0 -0
  98. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/s3_buckets/s3_bucket_replication_source_construct.py +0 -0
  99. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/constructs/sqs/policies/sqs_policies.py +0 -0
  100. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -0
  101. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/interfaces/istack.py +0 -0
  102. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/interfaces/live_ssm_resolver.py +0 -0
  103. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/interfaces/ssm_parameter_mixin.py +0 -0
  104. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/lambdas/health_handler.py +0 -0
  105. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/pipeline/path_utils.py +0 -0
  106. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/pipeline/pipeline_factory.py +0 -0
  107. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/pipeline/security/policies.py +0 -0
  108. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/pipeline/security/roles.py +0 -0
  109. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/pipeline/stage.py +0 -0
  110. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack/istack.py +0 -0
  111. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack/stack_factory.py +0 -0
  112. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack/stack_module_loader.py +0 -0
  113. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack/stack_module_registry.py +0 -0
  114. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack/stack_modules.py +0 -0
  115. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/__init__.py +0 -0
  116. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/api_gateway/api_gateway_stack.py +0 -0
  117. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/auto_scaling/__init__.py +0 -0
  118. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/aws_lambdas/lambda_stack.py +0 -0
  119. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/buckets/README.md +0 -0
  120. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/buckets/bucket_stack.py +0 -0
  121. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/code_artifact/code_artifact_stack.py +0 -0
  122. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/cognito/cognito_stack.py +0 -0
  123. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/dynamodb/dynamodb_stack.py +0 -0
  124. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/ecr/README.md +0 -0
  125. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/ecr/ecr_stack.py +0 -0
  126. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/ecs/__init__.py +0 -0
  127. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/load_balancer/__init__.py +0 -0
  128. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/load_balancer/load_balancer_stack.py +0 -0
  129. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/rds/__init__.py +0 -0
  130. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/rds/rds_stack.py +0 -0
  131. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/route53/__init__.py +0 -0
  132. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/route53/route53_stack.py +0 -0
  133. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/rum/__init__.py +0 -0
  134. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/rum/rum_stack.py +0 -0
  135. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/security_group/__init__.py +0 -0
  136. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/security_group/security_group_full_stack.py +0 -0
  137. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/security_group/security_group_stack.py +0 -0
  138. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/simple_queue_service/sqs_stack.py +0 -0
  139. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/stack_base.py +0 -0
  140. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/vpc/__init__.py +0 -0
  141. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/vpc/vpc_stack.py +0 -0
  142. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stack_library/websites/static_website_stack.py +0 -0
  143. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/stages/websites/static_website_stage.py +0 -0
  144. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/templates/README.md +0 -0
  145. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/templates/app.py.template +0 -0
  146. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/templates/cdk.json.template +0 -0
  147. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/api_gateway_integration_utility.py +0 -0
  148. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/commandline_args.py +0 -0
  149. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/configuration_loader.py +0 -0
  150. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/docker_utilities.py +0 -0
  151. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/environment_services.py +0 -0
  152. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/file_operations.py +0 -0
  153. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/git_utilities.py +0 -0
  154. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/json_loading_utility.py +0 -0
  155. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/lambda_function_utilities.py +0 -0
  156. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utilities/os_execute.py +0 -0
  157. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/cdk_factory/utils/api_gateway_utilities.py +0 -0
  158. {cdk_factory-0.9.12 → cdk_factory-0.10.0}/src/handlers/test/handler.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cdk_factory
3
- Version: 0.9.12
3
+ Version: 0.10.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
@@ -33,7 +33,7 @@ markers = [
33
33
  [project]
34
34
 
35
35
  name = "cdk_factory"
36
- version = "0.9.12"
36
+ version = "0.10.0"
37
37
  authors = [
38
38
  { name="Eric Wilson", email="eric.wilson@geekcafe.com" }
39
39
  ]
@@ -24,15 +24,19 @@ echo "=================================="
24
24
  if [ ! -d ".venv" ]; then
25
25
  echo -e "${YELLOW}Creating virtual environment...${NC}"
26
26
  python3 -m venv .venv
27
+ # Install dependencies
28
+ echo -e "${YELLOW}Installing dependencies...${NC}"
29
+ pip install -q -r requirements.dev.txt
30
+ pip install -q -r requirements.tests.txt
31
+ fi
32
+
33
+ # see if it's activated
34
+ if [ ! -f ".venv/bin/activate" ]; then
35
+ echo -e "${YELLOW}Activating virtual environment...${NC}"
36
+ source ./.venv/bin/activate
27
37
  fi
28
38
 
29
- echo -e "${YELLOW}Activating virtual environment...${NC}"
30
- source ./.venv/bin/activate
31
39
 
32
- # Install dependencies
33
- echo -e "${YELLOW}Installing dependencies...${NC}"
34
- pip install -q -r requirements.dev.txt
35
- pip install -q -r requirements.tests.txt
36
40
 
37
41
  # Check if pytest is installed in the virtual environment
38
42
  if ! command -v pytest &> /dev/null; then
@@ -148,3 +148,30 @@ class AutoScalingConfig(EnhancedBaseConfig):
148
148
  def target_group_arns(self) -> List[str]:
149
149
  """Target group ARNs for the Auto Scaling Group"""
150
150
  return self.__config.get("target_group_arns", [])
151
+
152
+ @property
153
+ def user_data_scripts(self) -> List[Dict[str, Any]]:
154
+ """
155
+ User data scripts to inject from files.
156
+ Each script should have:
157
+ - type: 'file' or 'inline'
158
+ - path: path to script file (if type is 'file')
159
+ - content: script content (if type is 'inline')
160
+ - variables: dict of variables for substitution
161
+ """
162
+ return self.__config.get("user_data_scripts", [])
163
+
164
+ @property
165
+ def iam_inline_policies(self) -> List[Dict[str, Any]]:
166
+ """
167
+ IAM inline policies to attach to the instance role.
168
+ Each policy should have:
169
+ - name: policy name
170
+ - statements: list of IAM policy statements
171
+ """
172
+ return self.__config.get("iam_inline_policies", [])
173
+
174
+ @property
175
+ def key_name(self) -> Optional[str]:
176
+ """EC2 key pair name for SSH access"""
177
+ return self.__config.get("key_name")
@@ -0,0 +1,124 @@
1
+ """
2
+ Geek Cafe, LLC
3
+ Maintainers: Eric Wilson
4
+ MIT License. See Project Root for the license information.
5
+ """
6
+ from typing import Dict, List, Any, Optional
7
+ from cdk_factory.configurations.enhanced_base_config import EnhancedBaseConfig
8
+
9
+
10
+ class CloudFrontConfig(EnhancedBaseConfig):
11
+ """
12
+ CloudFront Distribution Configuration
13
+ Supports both S3 origins (static sites) and custom origins (ALB, API Gateway, etc.)
14
+ """
15
+
16
+ def __init__(self, config: dict = None, deployment=None) -> None:
17
+ super().__init__(
18
+ config or {},
19
+ resource_type="cloudfront",
20
+ resource_name=config.get("name", "cloudfront") if config else "cloudfront"
21
+ )
22
+ self._config = config or {}
23
+ self._deployment = deployment
24
+
25
+ @property
26
+ def name(self) -> str:
27
+ """Distribution name"""
28
+ return self._config.get("name", "cloudfront")
29
+
30
+ @property
31
+ def description(self) -> str:
32
+ """Distribution description"""
33
+ return self._config.get("description", "CloudFront Distribution")
34
+
35
+ @property
36
+ def comment(self) -> str:
37
+ """Distribution comment"""
38
+ return self._config.get("comment", "")
39
+
40
+ @property
41
+ def enabled(self) -> bool:
42
+ """Whether distribution is enabled"""
43
+ return self._config.get("enabled", True)
44
+
45
+ @property
46
+ def aliases(self) -> List[str]:
47
+ """Alternate domain names (CNAMEs)"""
48
+ return self._config.get("aliases", [])
49
+
50
+ @property
51
+ def price_class(self) -> str:
52
+ """Price class for edge locations"""
53
+ return self._config.get("price_class", "PriceClass_100")
54
+
55
+ @property
56
+ def http_version(self) -> str:
57
+ """HTTP version (http2, http2_and_3)"""
58
+ return self._config.get("http_version", "http2_and_3")
59
+
60
+ @property
61
+ def certificate(self) -> Optional[Dict[str, Any]]:
62
+ """ACM certificate configuration"""
63
+ return self._config.get("certificate")
64
+
65
+ @property
66
+ def origins(self) -> List[Dict[str, Any]]:
67
+ """Origin configurations"""
68
+ return self._config.get("origins", [])
69
+
70
+ @property
71
+ def default_cache_behavior(self) -> Dict[str, Any]:
72
+ """Default cache behavior"""
73
+ return self._config.get("default_cache_behavior", {})
74
+
75
+ @property
76
+ def cache_behaviors(self) -> List[Dict[str, Any]]:
77
+ """Additional cache behaviors"""
78
+ return self._config.get("cache_behaviors", [])
79
+
80
+ @property
81
+ def custom_error_responses(self) -> List[Dict[str, Any]]:
82
+ """Custom error responses"""
83
+ return self._config.get("custom_error_responses", [])
84
+
85
+ @property
86
+ def logging(self) -> Optional[Dict[str, Any]]:
87
+ """Logging configuration"""
88
+ return self._config.get("logging")
89
+
90
+ @property
91
+ def waf_web_acl_id(self) -> Optional[str]:
92
+ """WAF Web ACL ID"""
93
+ return self._config.get("waf_web_acl_id")
94
+
95
+ @property
96
+ def default_root_object(self) -> str:
97
+ """Default root object"""
98
+ return self._config.get("default_root_object", "index.html")
99
+
100
+ @property
101
+ def tags(self) -> Dict[str, str]:
102
+ """Resource tags"""
103
+ return self._config.get("tags", {})
104
+
105
+ @property
106
+ def ssm_exports(self) -> Dict[str, str]:
107
+ """SSM parameter exports"""
108
+ return self._config.get("ssm_exports", {})
109
+
110
+ @property
111
+ def ssm_imports(self) -> Dict[str, str]:
112
+ """SSM parameter imports"""
113
+ return self._config.get("ssm_imports", {})
114
+
115
+ @property
116
+ def hosted_zone_id(self) -> str:
117
+ """
118
+ Returns the hosted_zone_id for cloudfront
119
+ Use this when making dns changes when you want your custom domain
120
+ to be route through cloudfront.
121
+
122
+ As far as I know this Id is static and used for all of cloudfront
123
+ """
124
+ return self._config.get("hosted_zone_id", "Z2FDTNDATAQYW2")
@@ -142,3 +142,15 @@ class EcsServiceConfig:
142
142
  def is_maintenance_mode(self) -> bool:
143
143
  """Whether this is a maintenance mode deployment"""
144
144
  return self.deployment_type == "maintenance"
145
+
146
+ @property
147
+ def volumes(self) -> List[Dict[str, Any]]:
148
+ """
149
+ Volume definitions for the task.
150
+ Supports host volumes for EC2 launch type and EFS volumes.
151
+ Each volume should have:
152
+ - name: volume name
153
+ - host: {source_path: "/path/on/host"} for bind mounts
154
+ - efs: {...} for EFS volumes
155
+ """
156
+ return self.task_definition.get("volumes", [])
@@ -0,0 +1,92 @@
1
+ """
2
+ Lambda@Edge Configuration for CDK-Factory
3
+ Geek Cafe, LLC
4
+ Maintainers: Eric Wilson
5
+ MIT License. See Project Root for the license information.
6
+ """
7
+ from typing import Dict, Optional
8
+ from cdk_factory.configurations.enhanced_base_config import EnhancedBaseConfig
9
+
10
+
11
+ class LambdaEdgeConfig(EnhancedBaseConfig):
12
+ """
13
+ Configuration class for Lambda@Edge functions.
14
+ Lambda@Edge has specific constraints:
15
+ - Must be deployed in us-east-1
16
+ - Max timeout: 5 seconds for origin-request/response, 30s for viewer-request/response
17
+ - Max memory: 10GB (but typically use 128-512MB for edge functions)
18
+ - Must use versioned functions (not $LATEST)
19
+ """
20
+
21
+ def __init__(self, config: dict = None, deployment=None) -> None:
22
+ super().__init__(
23
+ config or {},
24
+ resource_type="lambda_edge",
25
+ resource_name=config.get("name", "lambda-edge") if config else "lambda-edge"
26
+ )
27
+ self._config = config or {}
28
+ self._deployment = deployment
29
+
30
+ @property
31
+ def name(self) -> str:
32
+ """Function name"""
33
+ return self._config.get("name", "lambda-edge")
34
+
35
+ @property
36
+ def handler(self) -> str:
37
+ """Handler function (e.g., 'handler.lambda_handler')"""
38
+ return self._config.get("handler", "handler.lambda_handler")
39
+
40
+ @property
41
+ def runtime(self) -> str:
42
+ """Lambda runtime (e.g., 'python3.11')"""
43
+ return self._config.get("runtime", "python3.11")
44
+
45
+ @property
46
+ def memory_size(self) -> int:
47
+ """Memory size in MB (128-10240)"""
48
+ return int(self._config.get("memory_size", 128))
49
+
50
+ @property
51
+ def timeout(self) -> int:
52
+ """Timeout in seconds (max 5 for origin-request)"""
53
+ timeout = int(self._config.get("timeout", 5))
54
+ if timeout > 5:
55
+ raise ValueError("Lambda@Edge origin-request timeout cannot exceed 5 seconds")
56
+ return timeout
57
+
58
+ @property
59
+ def code_path(self) -> str:
60
+ """Path to Lambda function code directory"""
61
+ return self._config.get("code_path", "./lambdas/edge/ip_gate")
62
+
63
+ @property
64
+ def environment(self) -> Dict[str, str]:
65
+ """Environment variables for the Lambda function"""
66
+ return self._config.get("environment", {})
67
+
68
+ @property
69
+ def description(self) -> str:
70
+ """Function description"""
71
+ return self._config.get("description", "Lambda@Edge function")
72
+
73
+ @property
74
+ def event_type(self) -> str:
75
+ """
76
+ Lambda@Edge event type:
77
+ - viewer-request: Executes when CloudFront receives a request from viewer
78
+ - origin-request: Executes before CloudFront forwards request to origin
79
+ - origin-response: Executes after CloudFront receives response from origin
80
+ - viewer-response: Executes before CloudFront returns response to viewer
81
+ """
82
+ return self._config.get("event_type", "origin-request")
83
+
84
+ @property
85
+ def publish_version(self) -> bool:
86
+ """Whether to publish a new version (required for Lambda@Edge)"""
87
+ return self._config.get("publish_version", True)
88
+
89
+ @property
90
+ def include_body(self) -> bool:
91
+ """Whether to include request body in origin-request events"""
92
+ return self._config.get("include_body", False)
@@ -0,0 +1,74 @@
1
+ """
2
+ Monitoring Configuration
3
+ Geek Cafe, LLC
4
+ Maintainers: Eric Wilson
5
+ MIT License. See Project Root for the license information.
6
+ """
7
+
8
+ from typing import Dict, List, Any, Optional
9
+ from cdk_factory.configurations.enhanced_base_config import EnhancedBaseConfig
10
+
11
+
12
+ class MonitoringConfig(EnhancedBaseConfig):
13
+ """
14
+ Monitoring Configuration for CloudWatch Alarms and Dashboards
15
+ """
16
+
17
+ def __init__(self, config: dict = None, deployment=None) -> None:
18
+ super().__init__(
19
+ config or {},
20
+ resource_type="monitoring",
21
+ resource_name=config.get("name", "monitoring") if config else "monitoring"
22
+ )
23
+ self._config = config or {}
24
+ self._deployment = deployment
25
+
26
+ @property
27
+ def name(self) -> str:
28
+ """Monitoring stack name"""
29
+ return self._config.get("name", "monitoring")
30
+
31
+ @property
32
+ def sns_topics(self) -> List[Dict[str, Any]]:
33
+ """SNS topics for alarm notifications"""
34
+ return self._config.get("sns_topics", [])
35
+
36
+ @property
37
+ def alarms(self) -> List[Dict[str, Any]]:
38
+ """CloudWatch alarms configuration"""
39
+ return self._config.get("alarms", [])
40
+
41
+ @property
42
+ def dashboards(self) -> List[Dict[str, Any]]:
43
+ """CloudWatch dashboards configuration"""
44
+ return self._config.get("dashboards", [])
45
+
46
+ @property
47
+ def composite_alarms(self) -> List[Dict[str, Any]]:
48
+ """Composite alarms (combine multiple alarms)"""
49
+ return self._config.get("composite_alarms", [])
50
+
51
+ @property
52
+ def log_metric_filters(self) -> List[Dict[str, Any]]:
53
+ """CloudWatch Logs metric filters"""
54
+ return self._config.get("log_metric_filters", [])
55
+
56
+ @property
57
+ def enable_anomaly_detection(self) -> bool:
58
+ """Enable CloudWatch anomaly detection"""
59
+ return self._config.get("enable_anomaly_detection", False)
60
+
61
+ @property
62
+ def tags(self) -> Dict[str, str]:
63
+ """Resource tags"""
64
+ return self._config.get("tags", {})
65
+
66
+ @property
67
+ def ssm_exports(self) -> Dict[str, str]:
68
+ """SSM parameter exports"""
69
+ return self._config.get("ssm_exports", {})
70
+
71
+ @property
72
+ def ssm_imports(self) -> Dict[str, str]:
73
+ """SSM parameter imports for resource ARNs"""
74
+ return self._config.get("ssm_imports", {})
@@ -1,10 +1,11 @@
1
- from typing import Any, List, Mapping
1
+ from typing import Any, List, Mapping, Optional
2
2
 
3
3
  from aws_cdk import Duration
4
4
  from aws_cdk import aws_certificatemanager as acm
5
5
  from aws_cdk import aws_cloudfront as cloudfront
6
6
  from aws_cdk import aws_cloudfront_origins as origins
7
7
  from aws_cdk import aws_iam as iam
8
+ from aws_cdk import aws_lambda as _lambda
8
9
  from aws_cdk import aws_s3 as s3
9
10
  from constructs import Construct
10
11
  from cdk_factory.configurations.stack import StackConfig
@@ -123,6 +124,7 @@ class CloudFrontDistributionConstruct(Construct):
123
124
  origin=origin,
124
125
  viewer_protocol_policy=cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
125
126
  function_associations=self.__get_function_associations(),
127
+ edge_lambdas=self.__get_lambda_edge_associations(),
126
128
  ),
127
129
  default_root_object="index.html",
128
130
  error_responses=self._error_responses(),
@@ -218,6 +220,54 @@ class CloudFrontDistributionConstruct(Construct):
218
220
 
219
221
  return function_associations
220
222
 
223
+ def __get_lambda_edge_associations(self) -> Optional[List[cloudfront.EdgeLambda]]:
224
+ """
225
+ Get the Lambda@Edge associations for the distribution from config.
226
+
227
+ Returns:
228
+ List[cloudfront.EdgeLambda] or None: list of Lambda@Edge associations
229
+ """
230
+ edge_lambdas = []
231
+
232
+ if self.stack_config and isinstance(self.stack_config, StackConfig):
233
+ cloudfront_config = self.stack_config.dictionary.get("cloudfront", {})
234
+ lambda_edge_associations = cloudfront_config.get("lambda_edge_associations", [])
235
+
236
+ for association in lambda_edge_associations:
237
+ event_type_str = association.get("event_type", "origin-request")
238
+ lambda_arn = association.get("lambda_arn")
239
+ include_body = association.get("include_body", False)
240
+
241
+ if not lambda_arn:
242
+ continue # Skip if no ARN provided
243
+
244
+ # Map event type string to CloudFront enum
245
+ event_type_map = {
246
+ "viewer-request": cloudfront.LambdaEdgeEventType.VIEWER_REQUEST,
247
+ "origin-request": cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST,
248
+ "origin-response": cloudfront.LambdaEdgeEventType.ORIGIN_RESPONSE,
249
+ "viewer-response": cloudfront.LambdaEdgeEventType.VIEWER_RESPONSE,
250
+ }
251
+
252
+ event_type = event_type_map.get(event_type_str, cloudfront.LambdaEdgeEventType.ORIGIN_REQUEST)
253
+
254
+ # Import the Lambda function version by ARN
255
+ lambda_version = _lambda.Version.from_version_arn(
256
+ self,
257
+ f"LambdaEdge-{event_type_str}",
258
+ version_arn=lambda_arn
259
+ )
260
+
261
+ edge_lambdas.append(
262
+ cloudfront.EdgeLambda(
263
+ function_version=lambda_version,
264
+ event_type=event_type,
265
+ include_body=include_body
266
+ )
267
+ )
268
+
269
+ return edge_lambdas if edge_lambdas else None
270
+
221
271
  def __get_combined_function(self, hosts: List[str]) -> cloudfront.Function:
222
272
  """
223
273
  Creates a combined CloudFront function that does both URL rewriting and host restrictions.
@@ -0,0 +1,104 @@
1
+ """
2
+ Lambda@Edge Origin-Request Handler for IP-based Access Gating
3
+ Geek Cafe, LLC
4
+ Maintainers: Eric Wilson
5
+ """
6
+
7
+ import ipaddress
8
+ import json
9
+ import os
10
+
11
+
12
+ def lambda_handler(event, context):
13
+ """
14
+ Lambda@Edge origin-request handler that implements IP-based gating
15
+ with maintenance site fallback.
16
+
17
+ Features:
18
+ - Inject X-Viewer-IP header for origin visibility
19
+ - Check viewer IP against allowlist when gate is enabled
20
+ - Rewrite blocked IPs to maintenance CloudFront distribution, and serve up maintenance site
21
+ - Toggle via GATE_ENABLED environment variable
22
+ """
23
+
24
+ # Extract request from CloudFront event
25
+ request = event['Records'][0]['cf']['request']
26
+ client_ip = request['clientIp']
27
+
28
+ # Configuration from environment variables
29
+ gate_enabled = os.environ.get('GATE_ENABLED', 'false').lower() == 'true'
30
+ allow_cidrs_str = os.environ.get('ALLOW_CIDRS', '')
31
+ maint_cf_host = os.environ.get('MAINT_CF_HOST', '')
32
+
33
+ # Parse allowed CIDRs
34
+ allow_cidrs = [cidr.strip() for cidr in allow_cidrs_str.split(',') if cidr.strip()]
35
+
36
+ # Always inject viewer IP header
37
+ if 'headers' not in request:
38
+ request['headers'] = {}
39
+
40
+ request['headers']['x-viewer-ip'] = [{
41
+ 'key': 'X-Viewer-IP',
42
+ 'value': client_ip
43
+ }]
44
+
45
+ # If gate is disabled, pass through to origin
46
+ if not gate_enabled:
47
+ return request
48
+
49
+ # Check if IP is in allowlist
50
+ ip_allowed = False
51
+ try:
52
+ client_ip_obj = ipaddress.ip_address(client_ip)
53
+ for cidr in allow_cidrs:
54
+ try:
55
+ network = ipaddress.ip_network(cidr, strict=False)
56
+ if client_ip_obj in network:
57
+ ip_allowed = True
58
+ break
59
+ except (ValueError, ipaddress.AddressValueError):
60
+ # Invalid CIDR, skip
61
+ continue
62
+ except (ValueError, ipaddress.AddressValueError):
63
+ # Invalid client IP, block by default
64
+ ip_allowed = False
65
+
66
+ # If IP is allowed, pass through to origin
67
+ if ip_allowed:
68
+ return request
69
+
70
+ # IP not allowed - redirect to maintenance site
71
+ if not maint_cf_host:
72
+ # Safety: if maintenance host not configured, pass through with warning
73
+ # In production, you might want to return a fixed error response instead
74
+ return request
75
+
76
+ # Rewrite origin to maintenance CloudFront distribution
77
+ request['origin'] = {
78
+ 'custom': {
79
+ 'domainName': maint_cf_host,
80
+ 'port': 443,
81
+ 'protocol': 'https',
82
+ 'path': '',
83
+ 'sslProtocols': ['TLSv1.2'],
84
+ 'readTimeout': 30,
85
+ 'keepaliveTimeout': 5,
86
+ 'customHeaders': {}
87
+ }
88
+ }
89
+
90
+ # Update Host header to match new origin
91
+ request['headers']['host'] = [{
92
+ 'key': 'Host',
93
+ 'value': maint_cf_host
94
+ }]
95
+
96
+ # Normalize URI - redirect directory requests to index.html
97
+ uri = request.get('uri', '/')
98
+ if uri.endswith('/'):
99
+ request['uri'] = uri + 'index.html'
100
+ elif '.' not in uri.split('/')[-1]:
101
+ # No file extension, likely a directory
102
+ request['uri'] = uri + '/index.html'
103
+
104
+ return request
@@ -193,6 +193,41 @@ class AutoScalingStack(IStack, EnhancedSsmParameterMixin):
193
193
  iam.ManagedPolicy.from_aws_managed_policy_name(policy_name)
194
194
  )
195
195
 
196
+ # Add inline policies (for custom permissions like S3 bucket access)
197
+ for policy_config in self.asg_config.iam_inline_policies:
198
+ policy_name = policy_config.get("name", "CustomPolicy")
199
+ statements = policy_config.get("statements", [])
200
+
201
+ if not statements:
202
+ logger.warning(f"No statements found for inline policy {policy_name}, skipping")
203
+ continue
204
+
205
+ # Build policy statements
206
+ policy_statements = []
207
+ for stmt in statements:
208
+ effect = iam.Effect.ALLOW if stmt.get("effect", "Allow") == "Allow" else iam.Effect.DENY
209
+ actions = stmt.get("actions", [])
210
+ resources = stmt.get("resources", [])
211
+
212
+ if not actions or not resources:
213
+ logger.warning(f"Incomplete statement in policy {policy_name}, skipping")
214
+ continue
215
+
216
+ policy_statements.append(
217
+ iam.PolicyStatement(
218
+ effect=effect,
219
+ actions=actions,
220
+ resources=resources
221
+ )
222
+ )
223
+
224
+ if policy_statements:
225
+ role.add_to_principal_policy(policy_statements[0])
226
+ for stmt in policy_statements[1:]:
227
+ role.add_to_principal_policy(stmt)
228
+
229
+ logger.info(f"Added inline policy {policy_name} with {len(policy_statements)} statements")
230
+
196
231
  return role
197
232
 
198
233
  def _create_user_data(self) -> ec2.UserData:
@@ -206,6 +241,10 @@ class AutoScalingStack(IStack, EnhancedSsmParameterMixin):
206
241
  for command in self.asg_config.user_data_commands:
207
242
  user_data.add_commands(command)
208
243
 
244
+ # Add user data scripts from files (with variable substitution)
245
+ if self.asg_config.user_data_scripts:
246
+ self._add_user_data_scripts_from_files(user_data)
247
+
209
248
  # Add container configuration if specified
210
249
  container_config = self.asg_config.container_config
211
250
  if container_config:
@@ -213,6 +252,66 @@ class AutoScalingStack(IStack, EnhancedSsmParameterMixin):
213
252
 
214
253
  return user_data
215
254
 
255
+ def _add_user_data_scripts_from_files(self, user_data: ec2.UserData) -> None:
256
+ """
257
+ Add user data scripts from external files with variable substitution.
258
+ Supports loading shell scripts and injecting them into user data with
259
+ placeholder replacement.
260
+ """
261
+ from pathlib import Path
262
+
263
+ for script_config in self.asg_config.user_data_scripts:
264
+ script_type = script_config.get("type", "file")
265
+
266
+ if script_type == "file":
267
+ # Load script from file
268
+ script_path = script_config.get("path")
269
+ if not script_path:
270
+ logger.warning("Script path not specified, skipping")
271
+ continue
272
+
273
+ # Resolve path (relative to project root or absolute)
274
+ path = Path(script_path)
275
+ if not path.is_absolute():
276
+ # Try relative to current working directory
277
+ path = Path.cwd() / script_path
278
+
279
+ if not path.exists():
280
+ logger.warning(f"Script file not found: {path}, skipping")
281
+ continue
282
+
283
+ # Read script content
284
+ try:
285
+ with open(path, 'r') as f:
286
+ script_content = f.read()
287
+ except Exception as e:
288
+ logger.error(f"Failed to read script file {path}: {e}")
289
+ continue
290
+
291
+ elif script_type == "inline":
292
+ # Use inline script content
293
+ script_content = script_config.get("content", "")
294
+ if not script_content:
295
+ logger.warning("Inline script content is empty, skipping")
296
+ continue
297
+ else:
298
+ logger.warning(f"Unknown script type: {script_type}, skipping")
299
+ continue
300
+
301
+ # Perform variable substitution
302
+ variables = script_config.get("variables", {})
303
+ for var_name, var_value in variables.items():
304
+ placeholder = f"{{{{{var_name}}}}}" # {{VAR_NAME}}
305
+ script_content = script_content.replace(placeholder, str(var_value))
306
+
307
+ # Add script to user data
308
+ # Split by lines and add each line as a command
309
+ for line in script_content.split('\n'):
310
+ if line.strip(): # Skip empty lines
311
+ user_data.add_commands(line)
312
+
313
+ logger.info(f"Added user data script from {script_type}: {script_config.get('path', 'inline')}")
314
+
216
315
  def _add_container_user_data(
217
316
  self, user_data: ec2.UserData, container_config: Dict[str, Any]
218
317
  ) -> None: