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.

Files changed (66) hide show
  1. cdk_factory/configurations/base_config.py +23 -24
  2. cdk_factory/configurations/cdk_config.py +1 -1
  3. cdk_factory/configurations/deployment.py +12 -0
  4. cdk_factory/configurations/devops.py +1 -1
  5. cdk_factory/configurations/resources/acm.py +9 -2
  6. cdk_factory/configurations/resources/auto_scaling.py +7 -5
  7. cdk_factory/configurations/resources/cloudfront.py +7 -2
  8. cdk_factory/configurations/resources/ecr.py +1 -1
  9. cdk_factory/configurations/resources/ecs_cluster.py +12 -5
  10. cdk_factory/configurations/resources/ecs_service.py +30 -3
  11. cdk_factory/configurations/resources/lambda_edge.py +18 -4
  12. cdk_factory/configurations/resources/load_balancer.py +8 -9
  13. cdk_factory/configurations/resources/monitoring.py +8 -3
  14. cdk_factory/configurations/resources/rds.py +8 -9
  15. cdk_factory/configurations/resources/route53.py +5 -0
  16. cdk_factory/configurations/resources/rum.py +7 -2
  17. cdk_factory/configurations/resources/s3.py +10 -2
  18. cdk_factory/configurations/resources/security_group_full_stack.py +7 -8
  19. cdk_factory/configurations/resources/vpc.py +19 -0
  20. cdk_factory/configurations/workload.py +32 -2
  21. cdk_factory/constructs/cloudfront/cloudfront_distribution_construct.py +1 -1
  22. cdk_factory/constructs/ecr/ecr_construct.py +9 -2
  23. cdk_factory/constructs/lambdas/policies/policy_docs.py +4 -4
  24. cdk_factory/interfaces/istack.py +4 -4
  25. cdk_factory/interfaces/networked_stack_mixin.py +6 -6
  26. cdk_factory/interfaces/standardized_ssm_mixin.py +684 -0
  27. cdk_factory/interfaces/vpc_provider_mixin.py +64 -33
  28. cdk_factory/lambdas/edge/ip_gate/handler.py +42 -40
  29. cdk_factory/pipeline/pipeline_factory.py +3 -3
  30. cdk_factory/stack_library/__init__.py +3 -2
  31. cdk_factory/stack_library/acm/acm_stack.py +7 -17
  32. cdk_factory/stack_library/api_gateway/api_gateway_stack.py +84 -59
  33. cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +454 -537
  34. cdk_factory/stack_library/cloudfront/cloudfront_stack.py +76 -22
  35. cdk_factory/stack_library/code_artifact/code_artifact_stack.py +5 -27
  36. cdk_factory/stack_library/cognito/cognito_stack.py +152 -92
  37. cdk_factory/stack_library/dynamodb/dynamodb_stack.py +19 -15
  38. cdk_factory/stack_library/ecr/ecr_stack.py +2 -2
  39. cdk_factory/stack_library/ecs/__init__.py +1 -3
  40. cdk_factory/stack_library/ecs/ecs_cluster_stack.py +159 -75
  41. cdk_factory/stack_library/ecs/ecs_service_stack.py +59 -52
  42. cdk_factory/stack_library/lambda_edge/EDGE_LOG_RETENTION_TODO.md +226 -0
  43. cdk_factory/stack_library/lambda_edge/LAMBDA_EDGE_LOG_RETENTION_BLOG.md +215 -0
  44. cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +240 -83
  45. cdk_factory/stack_library/load_balancer/load_balancer_stack.py +139 -212
  46. cdk_factory/stack_library/rds/rds_stack.py +74 -98
  47. cdk_factory/stack_library/route53/route53_stack.py +246 -40
  48. cdk_factory/stack_library/rum/rum_stack.py +108 -91
  49. cdk_factory/stack_library/security_group/security_group_full_stack.py +10 -53
  50. cdk_factory/stack_library/security_group/security_group_stack.py +12 -19
  51. cdk_factory/stack_library/simple_queue_service/sqs_stack.py +1 -34
  52. cdk_factory/stack_library/stack_base.py +5 -0
  53. cdk_factory/stack_library/vpc/vpc_stack.py +171 -130
  54. cdk_factory/stack_library/websites/static_website_stack.py +7 -3
  55. cdk_factory/utilities/api_gateway_integration_utility.py +24 -16
  56. cdk_factory/utilities/environment_services.py +5 -5
  57. cdk_factory/utilities/json_loading_utility.py +1 -1
  58. cdk_factory/validation/config_validator.py +483 -0
  59. cdk_factory/version.py +1 -1
  60. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/METADATA +1 -1
  61. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/RECORD +64 -62
  62. cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -321
  63. cdk_factory/interfaces/ssm_parameter_mixin.py +0 -454
  64. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/WHEEL +0 -0
  65. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/entry_points.txt +0 -0
  66. {cdk_factory-0.16.15.dist-info → cdk_factory-0.20.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,8 @@
1
1
  """
2
- ECS Cluster Stack Module
2
+ ECS Cluster Stack Module (Standardized SSM Version)
3
3
 
4
4
  Provides a dedicated stack for creating and configuring ECS clusters
5
- with proper configurability and explicit resource management.
5
+ with proper configurability, explicit resource management, and standardized SSM integration.
6
6
  """
7
7
 
8
8
  import logging
@@ -10,6 +10,7 @@ from typing import Optional, Dict, Any
10
10
 
11
11
  from aws_cdk import (
12
12
  aws_ecs as ecs,
13
+ aws_ec2 as ec2,
13
14
  aws_iam as iam,
14
15
  CfnOutput,
15
16
  )
@@ -19,6 +20,7 @@ from cdk_factory.configurations.stack import StackConfig
19
20
  from cdk_factory.configurations.deployment import DeploymentConfig
20
21
  from cdk_factory.configurations.workload import WorkloadConfig
21
22
  from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
23
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
22
24
  from cdk_factory.configurations.resources.ecs_cluster import EcsClusterConfig
23
25
  from cdk_factory.stack.stack_module_registry import register_stack
24
26
  from cdk_factory.interfaces.istack import IStack
@@ -27,16 +29,18 @@ logger = logging.getLogger(__name__)
27
29
 
28
30
 
29
31
  @register_stack("ecs_cluster_stack")
30
- class EcsClusterStack(IStack, VPCProviderMixin):
32
+ class EcsClusterStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
31
33
  """
32
- A dedicated stack for creating and managing ECS clusters.
34
+ A dedicated stack for creating and managing ECS clusters with standardized SSM integration.
33
35
 
34
36
  This stack provides explicit configuration of ECS clusters including:
35
37
  - Cluster naming
36
38
  - Container insights
37
39
  - Cluster settings
38
- - SSM parameter exports
40
+ - Standardized SSM parameter exports
39
41
  - IAM role configurations
42
+ - Template variable resolution
43
+ - Comprehensive validation
40
44
  """
41
45
 
42
46
  def __init__(self, scope: Construct, id: str, **kwargs) -> None:
@@ -58,11 +62,6 @@ class EcsClusterStack(IStack, VPCProviderMixin):
58
62
  self.ecs_cluster: Optional[ecs.Cluster] = None
59
63
  self.instance_role: Optional[iam.Role] = None
60
64
  self.instance_profile: Optional[iam.CfnInstanceProfile] = None
61
-
62
- # SSM imported values
63
- self.ssm_imported_values: Dict[str, Any] = {}
64
-
65
-
66
65
 
67
66
  def build(
68
67
  self,
@@ -87,15 +86,30 @@ class EcsClusterStack(IStack, VPCProviderMixin):
87
86
  # Initialize VPC cache from mixin
88
87
  self._initialize_vpc_cache()
89
88
 
90
- # Load ECS cluster configuration
91
- self.ecs_config: EcsClusterConfig = EcsClusterConfig(
92
- stack_config.dictionary.get("ecs_cluster", {})
93
- )
89
+ # Load ECS cluster configuration with full stack config for SSM access
90
+ ecs_cluster_dict = stack_config.dictionary.get("ecs_cluster", {})
91
+ # Merge SSM config from root level into ECS config for VPC resolution
92
+ if "ssm" in stack_config.dictionary:
93
+ ecs_cluster_dict["ssm"] = stack_config.dictionary["ssm"]
94
94
 
95
- logger.info(f"Creating ECS Cluster stack: {self.stack_name}")
95
+ self.ecs_config: EcsClusterConfig = EcsClusterConfig(ecs_cluster_dict)
96
96
 
97
- # Process SSM imports first
98
- self.process_ssm_imports(self.ecs_config, deployment, "ECS Cluster")
97
+ cluster_name = deployment.build_resource_name(self.ecs_config.name)
98
+
99
+ logger.info(f"Creating ECS Cluster stack: {cluster_name}")
100
+
101
+ # Setup standardized SSM integration
102
+ self.setup_ssm_integration(
103
+ scope=self,
104
+ config=self.ecs_config,
105
+ resource_type="ecs_cluster",
106
+ resource_name=cluster_name,
107
+ deployment=deployment,
108
+ workload=workload
109
+ )
110
+
111
+ # Process SSM imports using standardized method
112
+ self.process_ssm_imports()
99
113
 
100
114
  # Create the ECS cluster
101
115
  self._create_ecs_cluster()
@@ -106,7 +120,12 @@ class EcsClusterStack(IStack, VPCProviderMixin):
106
120
  # Export cluster information
107
121
  self._export_cluster_info()
108
122
 
109
- logger.info(f"ECS Cluster stack created: {self.stack_name}")
123
+ # Export SSM parameters
124
+ logger.info("Starting SSM parameter export for ECS cluster")
125
+ self._export_ssm_parameters()
126
+ logger.info("Completed SSM parameter export for ECS cluster")
127
+
128
+ logger.info(f"ECS Cluster stack created: {cluster_name}")
110
129
 
111
130
  def _create_ecs_cluster(self):
112
131
  """Create the ECS cluster with explicit configuration."""
@@ -117,25 +136,22 @@ class EcsClusterStack(IStack, VPCProviderMixin):
117
136
 
118
137
  # Add container insights if enabled
119
138
  if self.ecs_config.container_insights:
120
- cluster_settings.append({"name": "containerInsights", "value": "enabled"})
139
+ cluster_settings.append({"name": "containerInsightsV2", "value": "enabled"})
121
140
 
122
141
  # Add custom cluster settings
123
142
  if self.ecs_config.cluster_settings:
124
143
  cluster_settings.extend(self.ecs_config.cluster_settings)
125
144
 
126
- # Create the ECS cluster
127
- self.vpc = self.resolve_vpc(
128
- config=self.ecs_config,
129
- deployment=self.deployment,
130
- workload=self.workload
131
- )
145
+ # Get VPC using standardized approach
146
+ self.vpc = self._get_vpc()
132
147
 
148
+ # Create the ECS cluster
133
149
  self.ecs_cluster = ecs.Cluster(
134
150
  self,
135
151
  "ECSCluster",
136
152
  cluster_name=self.ecs_config.name,
137
153
  vpc=self.vpc,
138
- container_insights=self.ecs_config.container_insights,
154
+ container_insights_v2=ecs.ContainerInsights.ENABLED if self.ecs_config.container_insights else ecs.ContainerInsights.DISABLED,
139
155
  default_cloud_map_namespace=(
140
156
  self.ecs_config.cloud_map_namespace
141
157
  if self.ecs_config.cloud_map_namespace
@@ -148,11 +164,26 @@ class EcsClusterStack(IStack, VPCProviderMixin):
148
164
  ),
149
165
  )
150
166
 
151
- logger.info(f"Created ECS cluster: {self.ecs_config.name}")
167
+ logger.info(f"ECS cluster created: {self.ecs_config.name}")
168
+
169
+ def _get_vpc(self):
170
+ """
171
+ Get VPC using the centralized VPC provider mixin.
172
+ """
173
+
174
+ # Use the stack_config (not ecs_config) to ensure SSM imports are available
175
+ return self.resolve_vpc(
176
+ config=self.ecs_config,
177
+ deployment=self.deployment,
178
+ workload=self.workload
179
+ )
152
180
 
153
181
  def _create_iam_roles(self):
154
182
  """Create IAM roles for the ECS cluster if configured."""
183
+ logger.info(f"create_instance_role setting: {self.ecs_config.create_instance_role}")
184
+
155
185
  if not self.ecs_config.create_instance_role:
186
+ logger.info("Skipping instance role creation (disabled in config)")
156
187
  return
157
188
 
158
189
  logger.info("Creating ECS instance role")
@@ -162,71 +193,124 @@ class EcsClusterStack(IStack, VPCProviderMixin):
162
193
  self,
163
194
  "ECSInstanceRole",
164
195
  assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
165
- role_name=self.ecs_config.instance_role_name
166
- or f"{self.ecs_config.name}-instance-role",
196
+ managed_policies=[
197
+ iam.ManagedPolicy.from_aws_managed_policy_name("service-role/AmazonEC2ContainerServiceforEC2Role"),
198
+ iam.ManagedPolicy.from_aws_managed_policy_name("AmazonEC2ContainerRegistryReadOnly"),
199
+ iam.ManagedPolicy.from_aws_managed_policy_name("AmazonSSMManagedInstanceCore"),
200
+ ],
201
+ role_name=f"{self.ecs_config.name}-ecs-instance-role",
167
202
  )
168
203
 
169
- # Add managed policies
170
- for policy in self.ecs_config.managed_policies:
171
- self.instance_role.add_managed_policy(
172
- iam.ManagedPolicy.from_aws_managed_policy_name(policy)
173
- )
174
-
175
- # Add inline policies if provided
176
- if self.ecs_config.inline_policies:
177
- for policy_name, policy_document in self.ecs_config.inline_policies.items():
178
- self.instance_role.add_to_policy(
179
- iam.PolicyStatement.from_json(policy_document)
180
- )
204
+ logger.info(f"Created ECS instance role: {self.instance_role.role_name}")
181
205
 
182
206
  # Create instance profile
183
207
  self.instance_profile = iam.CfnInstanceProfile(
184
208
  self,
185
209
  "ECSInstanceProfile",
210
+ instance_profile_name=f"{self.ecs_config.name}-ecs-instance-profile",
186
211
  roles=[self.instance_role.role_name],
187
- instance_profile_name=self.ecs_config.instance_profile_name
188
- or f"{self.ecs_config.name}-instance-profile",
189
212
  )
190
213
 
191
- logger.info("Created ECS instance role and profile")
214
+ logger.info(f"Created ECS instance profile: {self.instance_profile.instance_profile_name}")
192
215
 
193
- def _export_cluster_info(self):
194
- """Export cluster information via SSM parameters and CloudFormation outputs."""
195
- logger.info("Exporting ECS cluster information")
216
+ logger.info("ECS instance role and profile created")
196
217
 
197
- ssm_exports = self.ecs_config.ssm_exports
198
-
199
- if not ssm_exports:
200
- logger.info("No SSM exports configured for ECS Cluster")
218
+ def _export_cluster_info(self):
219
+ """Export cluster information as CloudFormation outputs."""
220
+ if not self.ecs_cluster:
201
221
  return
202
222
 
203
- logger.info(f"Processing {len(ssm_exports)} SSM exports for ECS Cluster")
223
+ cluster_name = self.deployment.build_resource_name(self.ecs_config.name)
204
224
 
205
- if "cluster_name" in ssm_exports:
206
- self.export_ssm_parameter(
207
- scope=self,
208
- id="ClusterNameParam",
209
- value=self.ecs_config.name,
210
- parameter_name=ssm_exports["cluster_name"],
211
- description=f"ECS Cluster Name: {self.ecs_config.name}",
212
- )
225
+ # Export cluster name
226
+ CfnOutput(
227
+ self,
228
+ f"{cluster_name}-ClusterName",
229
+ value=self.ecs_cluster.cluster_name,
230
+ description=f"ECS Cluster Name for {cluster_name}",
231
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-ecs-cluster-name",
232
+ )
213
233
 
214
- if "cluster_arn" in ssm_exports:
215
- self.export_ssm_parameter(
216
- scope=self,
217
- id="ClusterArnParam",
218
- value=self.ecs_cluster.cluster_arn,
219
- parameter_name=ssm_exports["cluster_arn"],
220
- description=f"ECS Cluster ARN: {self.ecs_cluster.cluster_arn}",
221
- )
234
+ # Export cluster ARN
235
+ CfnOutput(
236
+ self,
237
+ f"{cluster_name}-ClusterArn",
238
+ value=self.ecs_cluster.cluster_arn,
239
+ description=f"ECS Cluster ARN for {cluster_name}",
240
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-ecs-cluster-arn",
241
+ )
222
242
 
223
- if "instance_role_arn" in ssm_exports:
224
- self.export_ssm_parameter(
225
- scope=self,
226
- id="InstanceRoleArnParam",
227
- value=self.instance_role.role_arn,
228
- parameter_name=ssm_exports["instance_role_arn"],
229
- description=f"ECS Instance Role ARN: {self.instance_role.role_arn}",
243
+ # Export security group if available
244
+ if hasattr(self.ecs_cluster, 'connections') and self.ecs_cluster.connections:
245
+ security_groups = self.ecs_cluster.connections.security_groups
246
+ if security_groups:
247
+ CfnOutput(
248
+ self,
249
+ f"{cluster_name}-SecurityGroupId",
250
+ value=security_groups[0].security_group_id,
251
+ description=f"ECS Cluster Security Group ID for {cluster_name}",
252
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-ecs-cluster-sg-id",
253
+ )
254
+
255
+ # Export instance profile if created
256
+ if self.instance_profile:
257
+ CfnOutput(
258
+ self,
259
+ f"{cluster_name}-InstanceProfileArn",
260
+ value=self.instance_profile.attr_arn,
261
+ description=f"ECS Instance Profile ARN for {cluster_name}",
262
+ export_name=f"{self.deployment.workload_name}-{self.deployment.environment}-ecs-instance-profile-arn",
230
263
  )
264
+
265
+ logger.info("ECS cluster information exported as outputs")
266
+
267
+ def _export_ssm_parameters(self) -> None:
268
+ """Export SSM parameters using standardized approach"""
269
+ logger.info("=== Starting SSM Parameter Export ===")
231
270
 
271
+ if not self.ecs_cluster:
272
+ logger.warning("No ECS cluster to export")
273
+ return
274
+
275
+ logger.info(f"ECS cluster found: {self.ecs_cluster.cluster_name}")
276
+ logger.info(f"SSM exports configured: {self.ssm_config.get('exports', {})}")
277
+
278
+ # Prepare resource values for export
279
+ resource_values = {
280
+ "cluster_name": self.ecs_cluster.cluster_name,
281
+ "cluster_arn": self.ecs_cluster.cluster_arn,
282
+ }
283
+
284
+ # Add instance role ARN if created
285
+ if self.instance_role:
286
+ resource_values["instance_role_arn"] = self.instance_role.role_arn
287
+ logger.info(f"Instance role ARN added: {self.instance_role.role_name}")
288
+ else:
289
+ logger.info("No instance role to export")
290
+
291
+ # Add security group ID if available
292
+ if hasattr(self.ecs_cluster, 'connections') and self.ecs_cluster.connections:
293
+ security_groups = self.ecs_cluster.connections.security_groups
294
+ if security_groups:
295
+ resource_values["security_group_id"] = security_groups[0].security_group_id
296
+ logger.info(f"Security group ID added: {security_groups[0].security_group_id}")
232
297
 
298
+ # Add instance profile ARN if created
299
+ if self.instance_profile:
300
+ resource_values["instance_profile_arn"] = self.instance_profile.attr_arn
301
+ logger.info(f"Instance profile ARN added: {self.instance_profile.instance_profile_name}")
302
+
303
+ # Export using standardized SSM mixin
304
+ logger.info(f"Resource values available for export: {list(resource_values.keys())}")
305
+ for key, value in resource_values.items():
306
+ logger.info(f" {key}: {value}")
307
+
308
+ try:
309
+ exported_params = self.export_ssm_parameters(resource_values)
310
+ logger.info(f"Successfully exported SSM parameters: {exported_params}")
311
+ except Exception as e:
312
+ logger.error(f"Failed to export SSM parameters: {str(e)}")
313
+ raise
314
+
315
+ # Backward compatibility alias
316
+ EcsClusterStackStandardized = EcsClusterStack
@@ -23,7 +23,8 @@ from cdk_factory.configurations.deployment import DeploymentConfig
23
23
  from cdk_factory.configurations.stack import StackConfig
24
24
  from cdk_factory.configurations.resources.ecs_service import EcsServiceConfig
25
25
  from cdk_factory.interfaces.istack import IStack
26
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
26
+ from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
27
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
27
28
  from cdk_factory.stack.stack_module_registry import register_stack
28
29
  from cdk_factory.workload.workload_factory import WorkloadConfig
29
30
 
@@ -33,7 +34,7 @@ logger = Logger(service="EcsServiceStack")
33
34
  @register_stack("ecs_service_library_module")
34
35
  @register_stack("ecs_service_stack")
35
36
  @register_stack("fargate_service_stack")
36
- class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
37
+ class EcsServiceStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
37
38
  """
38
39
  Reusable stack for ECS/Fargate services with Docker container support.
39
40
  Supports blue-green deployments, maintenance mode, and auto-scaling.
@@ -100,32 +101,16 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
100
101
 
101
102
  # Add outputs
102
103
  self._add_outputs(service_name)
104
+ self._export_to_ssm(service_name)
103
105
 
104
106
  def _load_vpc(self) -> None:
105
- """Load VPC from configuration"""
106
- # Check SSM imported values first
107
- if "vpc_id" in self.ssm_imported_values:
108
- vpc_id = self.ssm_imported_values["vpc_id"]
109
-
110
- # Build VPC attributes
111
- vpc_attrs = {
112
- "vpc_id": vpc_id,
113
- "availability_zones": ["us-east-1a", "us-east-1b"]
114
- }
115
-
116
- # Use from_vpc_attributes() for SSM tokens
117
- self._vpc = ec2.Vpc.from_vpc_attributes(self, "VPC", **vpc_attrs)
118
- else:
119
- vpc_id = self.ecs_config.vpc_id or self.workload.vpc_id
120
-
121
- if not vpc_id:
122
- raise ValueError("VPC ID is required for ECS service")
123
-
124
- self._vpc = ec2.Vpc.from_lookup(
125
- self,
126
- "VPC",
127
- vpc_id=vpc_id
128
- )
107
+ """Load VPC using the centralized VPC provider mixin."""
108
+ # Use the centralized VPC resolution from VPCProviderMixin
109
+ self._vpc = self.resolve_vpc(
110
+ config=self.ecs_config,
111
+ deployment=self.deployment,
112
+ workload=self.workload
113
+ )
129
114
 
130
115
  def _process_ssm_imports(self) -> None:
131
116
  """
@@ -241,6 +226,9 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
241
226
  "CloudWatchAgentServerPolicy"
242
227
  )
243
228
  )
229
+
230
+ # add any custom policies
231
+ self._add_custom_task_policies(task_role)
244
232
 
245
233
  # Create task definition based on launch type
246
234
  if self.ecs_config.launch_type == "EC2":
@@ -253,6 +241,7 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
253
241
  network_mode=ecs.NetworkMode(network_mode.upper()) if network_mode else ecs.NetworkMode.BRIDGE,
254
242
  execution_role=execution_role,
255
243
  task_role=task_role,
244
+ inference_accelerators=None
256
245
  )
257
246
  else:
258
247
  # Fargate task definition
@@ -264,6 +253,7 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
264
253
  memory_limit_mib=int(self.ecs_config.memory),
265
254
  execution_role=execution_role,
266
255
  task_role=task_role,
256
+ inference_accelerators=None
267
257
  )
268
258
 
269
259
  # Add volumes to task definition
@@ -272,6 +262,37 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
272
262
  # Add containers
273
263
  self._add_containers_to_task()
274
264
 
265
+ def _add_custom_task_policies(self, task_role: iam.Role) -> None:
266
+ """
267
+ Add custom task policies to the task definition.
268
+ """
269
+ for policy in self.ecs_config.task_definition.get("policies", []):
270
+
271
+ effect = policy.get("effect", "Allow")
272
+ action = policy.get("action", None)
273
+ actions = policy.get("actions", [])
274
+ if action:
275
+ actions.append(action)
276
+ resources = policy.get("resources", [])
277
+ resource = policy.get("resource", None)
278
+ if resource:
279
+ resources.append(resource)
280
+
281
+ if effect == "Allow" and actions:
282
+ effect = iam.Effect.ALLOW
283
+ if effect == "Deny" and actions:
284
+ effect = iam.Effect.DENY
285
+
286
+ sid = policy.get("sid", None)
287
+ task_role.add_to_policy(
288
+ iam.PolicyStatement(
289
+ effect=effect,
290
+ actions=actions,
291
+ resources=resources,
292
+ sid=sid,
293
+ )
294
+ )
295
+
275
296
  def _add_volumes_to_task(self) -> None:
276
297
  """
277
298
  Add volumes to the task definition.
@@ -556,6 +577,14 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
556
577
  """Attach service to load balancer target groups"""
557
578
  target_group_arns = self.ecs_config.target_group_arns
558
579
 
580
+ if target_group_arns:
581
+ tmp = []
582
+ for tg_arn in target_group_arns:
583
+ import hashlib
584
+ unique_id = hashlib.md5(tg_arn.encode()).hexdigest()
585
+ tmp.append(self.resolve_ssm_value(self, tg_arn, unique_id))
586
+ target_group_arns = tmp
587
+
559
588
  if not target_group_arns:
560
589
  # Try to load from SSM if configured
561
590
  target_group_arns = self._load_target_groups_from_ssm()
@@ -579,7 +608,7 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
579
608
  for param_key, param_name in ssm_imports.items():
580
609
  if 'target_group' in param_key.lower() or 'tg' in param_key.lower():
581
610
  try:
582
- param_value = self.get_ssm_parameter_value(param_name)
611
+ param_value = self.get_ssm_imported_value(param_name)
583
612
  if param_value and param_value.startswith('arn:'):
584
613
  target_group_arns.append(param_value)
585
614
  except Exception as e:
@@ -611,35 +640,13 @@ class EcsServiceStack(IStack, EnhancedSsmParameterMixin):
611
640
  scale_out_cooldown=cdk.Duration.seconds(60),
612
641
  )
613
642
 
643
+
644
+
614
645
  def _add_outputs(self, service_name: str) -> None:
615
646
  """Add CloudFormation outputs"""
647
+ return
616
648
 
617
- # Service name output
618
- cdk.CfnOutput(
619
- self,
620
- "ServiceName",
621
- value=self.service.service_name,
622
- description=f"ECS Service Name: {service_name}",
623
- )
624
649
 
625
- # Service ARN output
626
- cdk.CfnOutput(
627
- self,
628
- "ServiceArn",
629
- value=self.service.service_arn,
630
- description=f"ECS Service ARN: {service_name}",
631
- )
632
-
633
- # Cluster name output
634
- cdk.CfnOutput(
635
- self,
636
- "ClusterName",
637
- value=self.cluster.cluster_name,
638
- description=f"ECS Cluster Name for {service_name}",
639
- )
640
-
641
- # Export to SSM if configured
642
- self._export_to_ssm(service_name)
643
650
 
644
651
  def _export_to_ssm(self, service_name: str) -> None:
645
652
  """Export resource ARNs and names to SSM Parameter Store"""