cdk-factory 0.8.7__tar.gz → 0.8.8__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.

Potentially problematic release.


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

Files changed (152) hide show
  1. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/PKG-INFO +1 -1
  2. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/pyproject.toml +1 -1
  3. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/ecr.py +47 -2
  4. cdk_factory-0.8.8/src/cdk_factory/configurations/resources/ecs_service.py +144 -0
  5. cdk_factory-0.8.8/src/cdk_factory/constructs/ecr/ecr_construct.py +263 -0
  6. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/pipeline/pipeline_factory.py +13 -1
  7. cdk_factory-0.8.8/src/cdk_factory/stack_library/ecs/ecs_service_stack.py +525 -0
  8. cdk_factory-0.8.8/src/cdk_factory/stack_library/security_group/__init__.py +0 -0
  9. cdk_factory-0.8.8/src/cdk_factory/version.py +1 -0
  10. cdk_factory-0.8.7/src/cdk_factory/constructs/ecr/ecr_construct.py +0 -191
  11. cdk_factory-0.8.7/src/cdk_factory/version.py +0 -1
  12. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/.gitignore +0 -0
  13. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/.windsurfrules +0 -0
  14. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/BUG_FIX_SSM_IMPORTS_METADATA_FIELDS.md +0 -0
  15. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/CHANGELOG_v0.8.1.md +0 -0
  16. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/CHANGELOG_v0.8.2.md +0 -0
  17. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/CHANGELOG_v0.8.3.md +0 -0
  18. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/LAMBDA_PERMISSION_FIX_SUMMARY.md +0 -0
  19. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/LICENSE +0 -0
  20. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/README.md +0 -0
  21. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/RELEASE_NOTES_v0.8.2.md +0 -0
  22. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/SUMMARY_v0.8.2.md +0 -0
  23. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/SUMMARY_v0.8.3.md +0 -0
  24. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/archive/README.md +0 -0
  25. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/archive/migrate_to_enhanced_ssm.py +0 -0
  26. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/examples/json-imports/README.md +0 -0
  27. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/examples/separate-api-gateway/README.md +0 -0
  28. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/examples/separate-api-gateway/api-gateway-stack.json +0 -0
  29. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/examples/separate-api-gateway/config.json +0 -0
  30. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/examples/separate-api-gateway/lambda-stack.json +0 -0
  31. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/mypy.ini +0 -0
  32. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/publish_to_pypi.py +0 -0
  33. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/publish_to_pypi.sh +0 -0
  34. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/pysetup.py +0 -0
  35. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/pysetup.sh +0 -0
  36. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/requirements.dev.txt +0 -0
  37. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/requirements.tests.txt +0 -0
  38. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/requirements.txt +0 -0
  39. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/run-checks.sh +0 -0
  40. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/run-tests-clean-venv.sh +0 -0
  41. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/run-tests.sh +0 -0
  42. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/__init__.py +0 -0
  43. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/app.py +0 -0
  44. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/builds/README.md +0 -0
  45. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/cdk.json +0 -0
  46. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/commands/command_loader.py +0 -0
  47. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/base_config.py +0 -0
  48. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/cdk_config.py +0 -0
  49. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/deployment.py +0 -0
  50. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/deployment_wave.py +0 -0
  51. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/devops.py +0 -0
  52. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/enhanced_base_config.py +0 -0
  53. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/enhanced_ssm_config.py +0 -0
  54. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/management.py +0 -0
  55. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/pipeline.py +0 -0
  56. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/pipeline_stage.py +0 -0
  57. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/_resources.py +0 -0
  58. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/api_gateway.py +0 -0
  59. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/apigateway_route_config.py +0 -0
  60. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/auto_scaling.py +0 -0
  61. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/cloudfront.py +0 -0
  62. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/cloudwatch_widget.py +0 -0
  63. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/code_artifact.py +0 -0
  64. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/code_artifact_login.py +0 -0
  65. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/code_repository.py +0 -0
  66. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/cognito.py +0 -0
  67. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/docker.py +0 -0
  68. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/dynamodb.py +0 -0
  69. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/exisiting.py +0 -0
  70. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/lambda_function.py +0 -0
  71. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/lambda_layers.py +0 -0
  72. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/lambda_triggers.py +0 -0
  73. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/load_balancer.py +0 -0
  74. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/rds.py +0 -0
  75. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/resource_mapping.py +0 -0
  76. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/resource_naming.py +0 -0
  77. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/resource_types.py +0 -0
  78. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/route53.py +0 -0
  79. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/route53_hosted_zone.py +0 -0
  80. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/rum.py +0 -0
  81. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/s3.py +0 -0
  82. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/security_group.py +0 -0
  83. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/security_group_full_stack.py +0 -0
  84. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/sqs.py +0 -0
  85. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/resources/vpc.py +0 -0
  86. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/stack.py +0 -0
  87. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/configurations/workload.py +0 -0
  88. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py +0 -0
  89. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/lambdas/lambda_function_construct.py +0 -0
  90. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/lambdas/lambda_function_docker_construct.py +0 -0
  91. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/lambdas/lambda_function_role_construct.py +0 -0
  92. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/lambdas/policies/policy_docs.py +0 -0
  93. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/lambdas/policies/policy_statements.py +0 -0
  94. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/s3_buckets/s3_bucket_construct.py +0 -0
  95. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/s3_buckets/s3_bucket_replication_destination_construct.py +0 -0
  96. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/s3_buckets/s3_bucket_replication_source_construct.py +0 -0
  97. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/constructs/sqs/policies/sqs_policies.py +0 -0
  98. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -0
  99. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/interfaces/istack.py +0 -0
  100. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/interfaces/live_ssm_resolver.py +0 -0
  101. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/interfaces/ssm_parameter_mixin.py +0 -0
  102. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/lambdas/health_handler.py +0 -0
  103. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/pipeline/security/policies.py +0 -0
  104. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/pipeline/security/roles.py +0 -0
  105. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/pipeline/stage.py +0 -0
  106. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack/istack.py +0 -0
  107. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack/stack_factory.py +0 -0
  108. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack/stack_module_loader.py +0 -0
  109. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack/stack_module_registry.py +0 -0
  110. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack/stack_modules.py +0 -0
  111. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/__init__.py +0 -0
  112. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/api_gateway/api_gateway_stack.py +0 -0
  113. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/auto_scaling/__init__.py +0 -0
  114. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +0 -0
  115. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/aws_lambdas/lambda_stack.py +0 -0
  116. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/buckets/README.md +0 -0
  117. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/buckets/bucket_stack.py +0 -0
  118. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/code_artifact/code_artifact_stack.py +0 -0
  119. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/cognito/cognito_stack.py +0 -0
  120. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/dynamodb/dynamodb_stack.py +0 -0
  121. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/ecr/README.md +0 -0
  122. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/ecr/ecr_stack.py +0 -0
  123. {cdk_factory-0.8.7/src/cdk_factory/stack_library/rds → cdk_factory-0.8.8/src/cdk_factory/stack_library/ecs}/__init__.py +0 -0
  124. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/load_balancer/__init__.py +0 -0
  125. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/load_balancer/load_balancer_stack.py +0 -0
  126. {cdk_factory-0.8.7/src/cdk_factory/stack_library/route53 → cdk_factory-0.8.8/src/cdk_factory/stack_library/rds}/__init__.py +0 -0
  127. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/rds/rds_stack.py +0 -0
  128. {cdk_factory-0.8.7/src/cdk_factory/stack_library/security_group → cdk_factory-0.8.8/src/cdk_factory/stack_library/route53}/__init__.py +0 -0
  129. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/route53/route53_stack.py +0 -0
  130. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/rum/__init__.py +0 -0
  131. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/rum/rum_stack.py +0 -0
  132. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/security_group/security_group_full_stack.py +0 -0
  133. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/security_group/security_group_stack.py +0 -0
  134. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/simple_queue_service/sqs_stack.py +0 -0
  135. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/stack_base.py +0 -0
  136. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/vpc/__init__.py +0 -0
  137. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/vpc/vpc_stack.py +0 -0
  138. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stack_library/websites/static_website_stack.py +0 -0
  139. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/stages/websites/static_website_stage.py +0 -0
  140. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/api_gateway_integration_utility.py +0 -0
  141. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/commandline_args.py +0 -0
  142. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/configuration_loader.py +0 -0
  143. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/docker_utilities.py +0 -0
  144. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/environment_services.py +0 -0
  145. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/file_operations.py +0 -0
  146. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/git_utilities.py +0 -0
  147. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/json_loading_utility.py +0 -0
  148. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/lambda_function_utilities.py +0 -0
  149. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utilities/os_execute.py +0 -0
  150. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/utils/api_gateway_utilities.py +0 -0
  151. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/src/cdk_factory/workload/workload_factory.py +0 -0
  152. {cdk_factory-0.8.7 → cdk_factory-0.8.8}/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.8.7
3
+ Version: 0.8.8
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.8.7"
36
+ version = "0.8.8"
37
37
  authors = [
38
38
  { name="Eric Wilson", email="eric.wilson@geekcafe.com" }
39
39
  ]
@@ -16,7 +16,7 @@ class ECRConfig(EnhancedBaseConfig):
16
16
  ) -> None:
17
17
  super().__init__(config, resource_type="ecr", resource_name=config.get("name", "ecr") if config else "ecr")
18
18
  self.__config = config
19
- self.__deployment = config
19
+ self.__deployment = deployment
20
20
  self.__ssm_prefix_template = config.get("ssm_prefix_template", None)
21
21
 
22
22
  @property
@@ -74,12 +74,13 @@ class ECRConfig(EnhancedBaseConfig):
74
74
  Clear out untagged images after x days. This helps save costs.
75
75
  Untagged images will stay forever if you don't clean them out.
76
76
  """
77
+ days = None
77
78
  if self.__config and isinstance(self.__config, dict):
78
79
  days = self.__config.get("auto_delete_untagged_images_in_days")
79
80
  if days:
80
81
  days = int(days)
81
82
 
82
- return None
83
+ return days
83
84
 
84
85
  @property
85
86
  def use_existing(self) -> bool:
@@ -118,6 +119,50 @@ class ECRConfig(EnhancedBaseConfig):
118
119
  if not value:
119
120
  raise RuntimeError("Region is not defined")
120
121
  return value
122
+
123
+ @property
124
+ def cross_account_access(self) -> dict:
125
+ """
126
+ Cross-account access configuration.
127
+
128
+ Example:
129
+ {
130
+ "enabled": true,
131
+ "accounts": ["123456789012", "987654321098"],
132
+ "services": [
133
+ {
134
+ "name": "lambda",
135
+ "actions": ["ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer"],
136
+ "condition": {
137
+ "StringLike": {
138
+ "aws:sourceArn": "arn:aws:lambda:*:*:function:*"
139
+ }
140
+ }
141
+ },
142
+ {
143
+ "name": "ecs-tasks",
144
+ "service_principal": "ecs-tasks.amazonaws.com",
145
+ "actions": ["ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer"]
146
+ },
147
+ {
148
+ "name": "codebuild",
149
+ "service_principal": "codebuild.amazonaws.com",
150
+ "actions": ["ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer", "ecr:BatchCheckLayerAvailability"]
151
+ }
152
+ ]
153
+ }
154
+ """
155
+ if self.__config and isinstance(self.__config, dict):
156
+ return self.__config.get("cross_account_access", {})
157
+ return {}
158
+
159
+ @property
160
+ def cross_account_enabled(self) -> bool:
161
+ """Whether cross-account access is explicitly enabled"""
162
+ access_config = self.cross_account_access
163
+ if access_config:
164
+ return str(access_config.get("enabled", "true")).lower() == "true"
165
+ return True # Default to enabled for backward compatibility
121
166
 
122
167
  # SSM properties are now inherited from EnhancedBaseConfig
123
168
  # Keeping these for any direct access patterns in existing code
@@ -0,0 +1,144 @@
1
+ """
2
+ ECS Service Configuration
3
+ Maintainers: Eric Wilson
4
+ MIT License. See Project Root for the license information.
5
+ """
6
+
7
+ from typing import Dict, Any, List, Optional
8
+
9
+
10
+ class EcsServiceConfig:
11
+ """ECS Service Configuration"""
12
+
13
+ def __init__(self, config: Dict[str, Any]) -> None:
14
+ self._config = config
15
+
16
+ @property
17
+ def name(self) -> str:
18
+ """Service name"""
19
+ return self._config.get("name", "")
20
+
21
+ @property
22
+ def cluster_name(self) -> Optional[str]:
23
+ """ECS Cluster name"""
24
+ return self._config.get("cluster_name")
25
+
26
+ @property
27
+ def task_definition(self) -> Dict[str, Any]:
28
+ """Task definition configuration"""
29
+ return self._config.get("task_definition", {})
30
+
31
+ @property
32
+ def container_definitions(self) -> List[Dict[str, Any]]:
33
+ """Container definitions"""
34
+ return self.task_definition.get("containers", [])
35
+
36
+ @property
37
+ def cpu(self) -> str:
38
+ """Task CPU units"""
39
+ return self.task_definition.get("cpu", "256")
40
+
41
+ @property
42
+ def memory(self) -> str:
43
+ """Task memory (MB)"""
44
+ return self.task_definition.get("memory", "512")
45
+
46
+ @property
47
+ def launch_type(self) -> str:
48
+ """Launch type: FARGATE or EC2"""
49
+ return self._config.get("launch_type", "FARGATE")
50
+
51
+ @property
52
+ def desired_count(self) -> int:
53
+ """Desired number of tasks"""
54
+ return self._config.get("desired_count", 2)
55
+
56
+ @property
57
+ def min_capacity(self) -> int:
58
+ """Minimum number of tasks"""
59
+ return self._config.get("min_capacity", 1)
60
+
61
+ @property
62
+ def max_capacity(self) -> int:
63
+ """Maximum number of tasks"""
64
+ return self._config.get("max_capacity", 4)
65
+
66
+ @property
67
+ def vpc_id(self) -> Optional[str]:
68
+ """VPC ID"""
69
+ return self._config.get("vpc_id")
70
+
71
+ @property
72
+ def subnet_group_name(self) -> Optional[str]:
73
+ """Subnet group name for service placement"""
74
+ return self._config.get("subnet_group_name")
75
+
76
+ @property
77
+ def security_group_ids(self) -> List[str]:
78
+ """Security group IDs"""
79
+ return self._config.get("security_group_ids", [])
80
+
81
+ @property
82
+ def assign_public_ip(self) -> bool:
83
+ """Whether to assign public IP addresses"""
84
+ return self._config.get("assign_public_ip", False)
85
+
86
+ @property
87
+ def target_group_arns(self) -> List[str]:
88
+ """Target group ARNs for load balancing"""
89
+ return self._config.get("target_group_arns", [])
90
+
91
+ @property
92
+ def container_port(self) -> int:
93
+ """Container port for load balancer"""
94
+ return self._config.get("container_port", 80)
95
+
96
+ @property
97
+ def health_check_grace_period(self) -> int:
98
+ """Health check grace period in seconds"""
99
+ return self._config.get("health_check_grace_period", 60)
100
+
101
+ @property
102
+ def enable_execute_command(self) -> bool:
103
+ """Enable ECS Exec for debugging"""
104
+ return self._config.get("enable_execute_command", False)
105
+
106
+ @property
107
+ def enable_auto_scaling(self) -> bool:
108
+ """Enable auto-scaling"""
109
+ return self._config.get("enable_auto_scaling", True)
110
+
111
+ @property
112
+ def auto_scaling_target_cpu(self) -> int:
113
+ """Target CPU utilization percentage for auto-scaling"""
114
+ return self._config.get("auto_scaling_target_cpu", 70)
115
+
116
+ @property
117
+ def auto_scaling_target_memory(self) -> int:
118
+ """Target memory utilization percentage for auto-scaling"""
119
+ return self._config.get("auto_scaling_target_memory", 80)
120
+
121
+ @property
122
+ def tags(self) -> Dict[str, str]:
123
+ """Resource tags"""
124
+ return self._config.get("tags", {})
125
+
126
+ @property
127
+ def ssm_exports(self) -> Dict[str, str]:
128
+ """SSM parameter exports"""
129
+ return self._config.get("ssm_exports", {})
130
+
131
+ @property
132
+ def ssm_imports(self) -> Dict[str, str]:
133
+ """SSM parameter imports"""
134
+ return self._config.get("ssm_imports", {})
135
+
136
+ @property
137
+ def deployment_type(self) -> str:
138
+ """Deployment type: production, maintenance, or blue-green"""
139
+ return self._config.get("deployment_type", "production")
140
+
141
+ @property
142
+ def is_maintenance_mode(self) -> bool:
143
+ """Whether this is a maintenance mode deployment"""
144
+ return self.deployment_type == "maintenance"
@@ -0,0 +1,263 @@
1
+ """
2
+ Geek Cafe, LLC
3
+ Maintainers: Eric Wilson
4
+ MIT License. See Project Root for the license information.
5
+ """
6
+
7
+ from typing import cast, Dict
8
+ from aws_cdk import Duration, RemovalPolicy, aws_ecr
9
+ from aws_cdk import CfnResource
10
+ from aws_cdk import aws_iam as iam
11
+ from aws_cdk import aws_ssm as ssm
12
+ from aws_lambda_powertools import Logger
13
+ from constructs import Construct, IConstruct
14
+ from cdk_factory.configurations.resources.resource_types import ResourceTypes
15
+ from cdk_factory.configurations.resources.ecr import ECRConfig as ECR
16
+ from cdk_factory.configurations.deployment import DeploymentConfig as Deployment
17
+ from cdk_factory.interfaces.ssm_parameter_mixin import SsmParameterMixin
18
+
19
+ logger = Logger(__name__)
20
+
21
+
22
+ class ECRConstruct(Construct, SsmParameterMixin):
23
+ def __init__(
24
+ self,
25
+ scope: Construct,
26
+ id: str,
27
+ *,
28
+ deployment: Deployment,
29
+ repo: ECR,
30
+ **kwargs,
31
+ ) -> None:
32
+ super().__init__(scope, id, **kwargs)
33
+
34
+ self.scope = scope
35
+ self.deployment = deployment
36
+ self.repo = repo
37
+ self.ecr_name = repo.name
38
+ self.image_scan_on_push = repo.image_scan_on_push
39
+ self.empty_on_delete = repo.empty_on_delete
40
+ self.auto_delete_untagged_images_in_days = (
41
+ repo.auto_delete_untagged_images_in_days
42
+ )
43
+
44
+ # set it all up
45
+ self.ecr = self.__create_ecr()
46
+ self.__set_life_cycle_rules()
47
+ self.__create_parameter_store_values()
48
+ self.__setup_cross_account_access_permissions()
49
+
50
+ def __create_ecr(self) -> aws_ecr.Repository:
51
+ # create the ecr repo
52
+ name = self.deployment.build_resource_name(
53
+ self.ecr_name, ResourceTypes.ECR_REPOSITORY
54
+ )
55
+ ecr_repository = aws_ecr.Repository(
56
+ scope=self,
57
+ id=self.deployment.build_resource_name(self.ecr_name),
58
+ repository_name=name,
59
+ # auto delete images after x days
60
+ # auto_delete_images=self.empty_on_delete,
61
+ # delete images when repo is destroyed
62
+ empty_on_delete=self.empty_on_delete,
63
+ # scan on push true/false
64
+ image_scan_on_push=self.image_scan_on_push,
65
+ # removal policy on delete destroy if empty on delete otherwise retain
66
+ removal_policy=(
67
+ RemovalPolicy.DESTROY if self.empty_on_delete else RemovalPolicy.RETAIN
68
+ ),
69
+ )
70
+
71
+ return ecr_repository
72
+
73
+ def __create_parameter_store_values(self):
74
+ """
75
+ Stores the ecr info in the parameter store for consumption in
76
+ other cdk stacks using the SsmParameterMixin.
77
+
78
+ This method uses the new configurable SSM parameter prefix system.
79
+ """
80
+ # Create a dictionary of resource values to export
81
+ resource_values = {
82
+ "name": self.ecr.repository_name,
83
+ "uri": self.ecr.repository_uri,
84
+ "arn": self.ecr.repository_arn
85
+ }
86
+
87
+ # Use the export_resource_to_ssm method from SsmParameterMixin
88
+ params = self.export_resource_to_ssm(
89
+ scope=self,
90
+ resource_values=resource_values,
91
+ config=self.repo, # Pass the ECRConfig object which has ssm_exports
92
+ resource_name=self.ecr_name,
93
+ resource_type="ecr",
94
+ context={
95
+ "deployment_name": self.deployment.name,
96
+ "environment": self.deployment.environment,
97
+ "workload_name": self.deployment.workload_name
98
+ }
99
+ )
100
+
101
+ # Add dependencies to ensure SSM parameters are created after the ECR repository
102
+ if params:
103
+ for param in params.values():
104
+ if param and hasattr(param, 'node') and param.node.default_child and isinstance(param.node.default_child, CfnResource):
105
+ param.node.default_child.add_dependency(
106
+ cast(CfnResource, self.ecr.node.default_child)
107
+ )
108
+
109
+ def __set_life_cycle_rules(self) -> None:
110
+ # Note: tag_pattern_list is deprecated and causes circular dependencies in CDK synthesis
111
+ # Only add lifecycle rule for untagged images if configured
112
+
113
+ if not self.auto_delete_untagged_images_in_days:
114
+ return None
115
+
116
+ days = self.auto_delete_untagged_images_in_days
117
+
118
+ logger.info(
119
+ f"Adding life cycle policy. Removing untagged images after {days} days"
120
+ )
121
+ # remove any untagged images after x days
122
+ self.ecr.add_lifecycle_rule(
123
+ tag_status=aws_ecr.TagStatus.UNTAGGED, max_image_age=Duration.days(days)
124
+ )
125
+
126
+ def __get_ecr(self) -> aws_ecr.IRepository:
127
+
128
+ return aws_ecr.Repository.from_repository_arn(
129
+ scope=self,
130
+ id=f"{self.deployment.build_resource_name(self.ecr_name)}-by-attribute",
131
+ # repository_name=self.ecr.repository_name,
132
+ repository_arn=self.ecr.repository_arn,
133
+ )
134
+
135
+ def __setup_cross_account_access_permissions(self):
136
+ """
137
+ Setup cross-account access permissions with flexible configuration support.
138
+
139
+ Supports both legacy (default Lambda access) and new configurable approach.
140
+ """
141
+ # Check if cross-account access is disabled
142
+ if not self.repo.cross_account_enabled:
143
+ logger.info(f"Cross-account access disabled for {self.ecr_name}")
144
+ return
145
+
146
+ # Check if we're in the same account as devops
147
+ if self.deployment.account == self.deployment.workload.get("devops", {}).get("account"):
148
+ logger.info(f"Same account as devops, skipping cross-account permissions for {self.ecr_name}")
149
+ return
150
+
151
+ access_config = self.repo.cross_account_access
152
+
153
+ if access_config and access_config.get("services"):
154
+ # New configurable approach
155
+ logger.info(f"Setting up configurable cross-account access for {self.ecr_name}")
156
+ self.__setup_configurable_access(access_config)
157
+ else:
158
+ # Legacy approach - default Lambda access for backward compatibility
159
+ logger.info(f"Setting up legacy cross-account access (Lambda only) for {self.ecr_name}")
160
+ self.__setup_legacy_lambda_access()
161
+
162
+ def __setup_configurable_access(self, access_config: dict):
163
+ """Setup cross-account access using configuration"""
164
+
165
+ # Get list of accounts (default to deployment account)
166
+ accounts = access_config.get("accounts", [self.deployment.account])
167
+
168
+ # Add account principal policies if accounts are specified
169
+ if accounts:
170
+ self.__add_account_principal_policy(accounts)
171
+
172
+ # Add service-specific policies
173
+ services = access_config.get("services", [])
174
+ for service_config in services:
175
+ self.__add_service_principal_policy(service_config)
176
+
177
+ def __add_account_principal_policy(self, accounts: list):
178
+ """Add policy for AWS account principals"""
179
+ principals = [iam.AccountPrincipal(account) for account in accounts]
180
+
181
+ policy_statement = iam.PolicyStatement(
182
+ actions=[
183
+ "ecr:GetDownloadUrlForLayer",
184
+ "ecr:BatchGetImage",
185
+ "ecr:BatchCheckLayerAvailability",
186
+ ],
187
+ principals=principals,
188
+ effect=iam.Effect.ALLOW,
189
+ )
190
+
191
+ response = self.ecr.add_to_resource_policy(policy_statement)
192
+ if not response.statement_added:
193
+ logger.warning(f"Failed to add account principal policy for {', '.join(accounts)}")
194
+ else:
195
+ logger.info(f"Added account principal policy for accounts: {', '.join(accounts)}")
196
+
197
+ def __add_service_principal_policy(self, service_config: dict):
198
+ """Add policy for service principal (Lambda, ECS, CodeBuild, etc.)"""
199
+ service_name = service_config.get("name", "unknown")
200
+ service_principal = service_config.get("service_principal")
201
+ actions = service_config.get("actions", ["ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer"])
202
+ conditions = service_config.get("condition")
203
+
204
+ if not service_principal:
205
+ # Infer service principal from common service names
206
+ service_principal_map = {
207
+ "lambda": "lambda.amazonaws.com",
208
+ "ecs": "ecs-tasks.amazonaws.com",
209
+ "ecs-tasks": "ecs-tasks.amazonaws.com",
210
+ "codebuild": "codebuild.amazonaws.com",
211
+ "codepipeline": "codepipeline.amazonaws.com",
212
+ "ec2": "ec2.amazonaws.com",
213
+ }
214
+ service_principal = service_principal_map.get(service_name.lower())
215
+
216
+ if not service_principal:
217
+ logger.warning(f"Unknown service principal for service: {service_name}")
218
+ return
219
+
220
+ policy_statement = iam.PolicyStatement(
221
+ effect=iam.Effect.ALLOW,
222
+ actions=actions,
223
+ principals=[iam.ServicePrincipal(service_principal)],
224
+ )
225
+
226
+ # Add conditions if specified
227
+ if conditions:
228
+ for condition_key, condition_value in conditions.items():
229
+ policy_statement.add_condition(condition_key, condition_value)
230
+
231
+ response = self.ecr.add_to_resource_policy(policy_statement)
232
+ if not response.statement_added:
233
+ logger.warning(f"Failed to add service principal policy for {service_name}")
234
+ else:
235
+ logger.info(f"Added service principal policy for {service_name} ({service_principal})")
236
+
237
+ def __setup_legacy_lambda_access(self):
238
+ """Legacy method: Setup default Lambda-only cross-account access"""
239
+
240
+ # Add account principal policy
241
+ self.__add_account_principal_policy([self.deployment.account])
242
+
243
+ # Add Lambda service principal policy with default condition
244
+ lambda_policy = iam.PolicyStatement(
245
+ effect=iam.Effect.ALLOW,
246
+ actions=["ecr:BatchGetImage", "ecr:GetDownloadUrlForLayer"],
247
+ principals=[iam.ServicePrincipal("lambda.amazonaws.com")],
248
+ )
249
+
250
+ lambda_policy.add_condition(
251
+ "StringLike",
252
+ {
253
+ "aws:sourceArn": [
254
+ f"arn:aws:lambda:{self.deployment.region}:{self.deployment.account}:function:*"
255
+ ]
256
+ }
257
+ )
258
+
259
+ response = self.ecr.add_to_resource_policy(lambda_policy)
260
+ if not response.statement_added:
261
+ logger.warning("Failed to add Lambda service principal policy")
262
+ else:
263
+ logger.info("Added legacy Lambda service principal policy")
@@ -246,7 +246,13 @@ class PipelineFactoryStack(cdk.Stack):
246
246
 
247
247
  def _get_steps(self, key: str, stage_config: PipelineStageConfig):
248
248
  """
249
- Gets the build steps from the config.json
249
+ Gets the build steps from the config.json.
250
+
251
+ Commands can be:
252
+ - A list of strings (each string is a separate command)
253
+ - A single multi-line string (treated as a single script block)
254
+
255
+ This allows support for complex shell constructs like if blocks, loops, etc.
250
256
  """
251
257
  shell_steps: List[pipelines.ShellStep] = []
252
258
 
@@ -257,6 +263,12 @@ class PipelineFactoryStack(cdk.Stack):
257
263
  for step in steps:
258
264
  step_id = step.get("id") or step.get("name")
259
265
  commands = step.get("commands", [])
266
+
267
+ # Normalize commands to a list
268
+ # If commands is a single string, wrap it in a list
269
+ if isinstance(commands, str):
270
+ commands = [commands]
271
+
260
272
  shell_step = pipelines.ShellStep(
261
273
  id=step_id,
262
274
  commands=commands,