cdk-factory 0.16.16__py3-none-any.whl → 0.17.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.
Files changed (57) hide show
  1. cdk_factory/configurations/base_config.py +23 -24
  2. cdk_factory/configurations/cdk_config.py +1 -1
  3. cdk_factory/configurations/devops.py +1 -1
  4. cdk_factory/configurations/resources/cloudfront.py +7 -2
  5. cdk_factory/configurations/resources/ecr.py +1 -1
  6. cdk_factory/configurations/resources/ecs_cluster.py +7 -5
  7. cdk_factory/configurations/resources/ecs_service.py +7 -2
  8. cdk_factory/configurations/resources/load_balancer.py +8 -9
  9. cdk_factory/configurations/resources/monitoring.py +8 -3
  10. cdk_factory/configurations/resources/rds.py +7 -8
  11. cdk_factory/configurations/resources/rum.py +7 -2
  12. cdk_factory/configurations/resources/s3.py +1 -1
  13. cdk_factory/configurations/resources/security_group_full_stack.py +7 -8
  14. cdk_factory/configurations/resources/vpc.py +19 -0
  15. cdk_factory/configurations/workload.py +32 -2
  16. cdk_factory/constructs/ecr/ecr_construct.py +9 -2
  17. cdk_factory/constructs/lambdas/policies/policy_docs.py +4 -4
  18. cdk_factory/interfaces/istack.py +4 -4
  19. cdk_factory/interfaces/networked_stack_mixin.py +6 -6
  20. cdk_factory/interfaces/standardized_ssm_mixin.py +612 -0
  21. cdk_factory/interfaces/vpc_provider_mixin.py +53 -29
  22. cdk_factory/lambdas/edge/ip_gate/handler.py +42 -40
  23. cdk_factory/pipeline/pipeline_factory.py +3 -3
  24. cdk_factory/stack_library/__init__.py +3 -2
  25. cdk_factory/stack_library/acm/acm_stack.py +2 -2
  26. cdk_factory/stack_library/api_gateway/api_gateway_stack.py +84 -59
  27. cdk_factory/stack_library/auto_scaling/auto_scaling_stack_standardized.py +530 -0
  28. cdk_factory/stack_library/code_artifact/code_artifact_stack.py +2 -2
  29. cdk_factory/stack_library/cognito/cognito_stack.py +152 -92
  30. cdk_factory/stack_library/dynamodb/dynamodb_stack.py +19 -15
  31. cdk_factory/stack_library/ecr/ecr_stack.py +2 -2
  32. cdk_factory/stack_library/ecs/__init__.py +1 -1
  33. cdk_factory/stack_library/ecs/ecs_cluster_stack_standardized.py +305 -0
  34. cdk_factory/stack_library/ecs/ecs_service_stack.py +10 -26
  35. cdk_factory/stack_library/lambda_edge/lambda_edge_stack.py +2 -2
  36. cdk_factory/stack_library/load_balancer/load_balancer_stack.py +11 -35
  37. cdk_factory/stack_library/rds/rds_stack.py +10 -27
  38. cdk_factory/stack_library/route53/route53_stack.py +2 -2
  39. cdk_factory/stack_library/rum/rum_stack.py +102 -91
  40. cdk_factory/stack_library/security_group/security_group_full_stack.py +9 -22
  41. cdk_factory/stack_library/security_group/security_group_stack.py +11 -11
  42. cdk_factory/stack_library/vpc/vpc_stack_standardized.py +411 -0
  43. cdk_factory/utilities/api_gateway_integration_utility.py +24 -16
  44. cdk_factory/utilities/environment_services.py +3 -3
  45. cdk_factory/utilities/json_loading_utility.py +1 -1
  46. cdk_factory/validation/config_validator.py +483 -0
  47. cdk_factory/version.py +1 -1
  48. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/METADATA +1 -1
  49. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/RECORD +52 -52
  50. cdk_factory/interfaces/enhanced_ssm_parameter_mixin.py +0 -321
  51. cdk_factory/interfaces/ssm_parameter_mixin.py +0 -454
  52. cdk_factory/stack_library/auto_scaling/auto_scaling_stack.py +0 -748
  53. cdk_factory/stack_library/ecs/ecs_cluster_stack.py +0 -232
  54. cdk_factory/stack_library/vpc/vpc_stack.py +0 -298
  55. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/WHEEL +0 -0
  56. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/entry_points.txt +0 -0
  57. {cdk_factory-0.16.16.dist-info → cdk_factory-0.17.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,530 @@
1
+ """
2
+ Auto Scaling Group Stack Pattern for CDK-Factory (Standardized SSM Version)
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
+ import aws_cdk as cdk
10
+ from aws_cdk import aws_ec2 as ec2
11
+ from aws_cdk import aws_autoscaling as autoscaling
12
+ from aws_cdk import aws_cloudwatch as cloudwatch
13
+ from aws_cdk import aws_iam as iam
14
+ from aws_cdk import aws_ssm as ssm
15
+ from aws_cdk import aws_ecs as ecs
16
+ from aws_cdk import Duration, Stack
17
+ from aws_lambda_powertools import Logger
18
+ from constructs import Construct
19
+
20
+ from cdk_factory.configurations.deployment import DeploymentConfig
21
+ from cdk_factory.configurations.stack import StackConfig
22
+ from cdk_factory.configurations.resources.auto_scaling import AutoScalingConfig
23
+ from cdk_factory.interfaces.istack import IStack
24
+ from cdk_factory.interfaces.vpc_provider_mixin import VPCProviderMixin
25
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
26
+ from cdk_factory.stack.stack_module_registry import register_stack
27
+ from cdk_factory.workload.workload_factory import WorkloadConfig
28
+
29
+ logger = Logger(service="AutoScalingStackStandardized")
30
+
31
+
32
+ @register_stack("auto_scaling_library_module")
33
+ @register_stack("auto_scaling_stack")
34
+ class AutoScalingStack(IStack, VPCProviderMixin, StandardizedSsmMixin):
35
+ """
36
+ Reusable stack for AWS Auto Scaling Groups with standardized SSM integration.
37
+
38
+ This version uses the StandardizedSsmMixin to provide consistent SSM parameter
39
+ handling across all CDK Factory modules.
40
+
41
+ Key Features:
42
+ - Standardized SSM import/export patterns
43
+ - Template variable resolution
44
+ - Comprehensive validation
45
+ - Clear error handling
46
+ - Backward compatibility
47
+ """
48
+
49
+ def __init__(self, scope: Construct, id: str, **kwargs) -> None:
50
+ # Initialize parent classes properly
51
+ super().__init__(scope, id, **kwargs)
52
+
53
+ # Initialize VPC cache from mixin
54
+ self._initialize_vpc_cache()
55
+
56
+ # Initialize module attributes
57
+ self.asg_config = None
58
+ self.stack_config = None
59
+ self.deployment = None
60
+ self.workload = None
61
+ self.security_groups = []
62
+ self.auto_scaling_group = None
63
+ self.launch_template = None
64
+ self.instance_role = None
65
+ self.user_data = None
66
+ self.user_data_commands = [] # Store raw commands for ECS cluster detection
67
+ self.ecs_cluster = None
68
+
69
+ def build(
70
+ self,
71
+ stack_config: StackConfig,
72
+ deployment: DeploymentConfig,
73
+ workload: WorkloadConfig,
74
+ ) -> None:
75
+ """Build the Auto Scaling Group stack"""
76
+ self._build(stack_config, deployment, workload)
77
+
78
+ def _build(
79
+ self,
80
+ stack_config: StackConfig,
81
+ deployment: DeploymentConfig,
82
+ workload: WorkloadConfig,
83
+ ) -> None:
84
+ """Internal build method for the Auto Scaling Group stack"""
85
+ self.stack_config = stack_config
86
+ self.deployment = deployment
87
+ self.workload = workload
88
+
89
+ self.asg_config = AutoScalingConfig(
90
+ stack_config.dictionary.get("auto_scaling", {}), deployment
91
+ )
92
+ asg_name = deployment.build_resource_name(self.asg_config.name)
93
+
94
+ # Setup standardized SSM integration
95
+ self.setup_standardized_ssm_integration(
96
+ scope=self,
97
+ config=self.asg_config,
98
+ resource_type="auto_scaling",
99
+ resource_name=asg_name,
100
+ deployment=deployment,
101
+ workload=workload
102
+ )
103
+
104
+ # Process SSM imports using standardized method
105
+ self.process_standardized_ssm_imports()
106
+
107
+ # Get security groups using standardized approach
108
+ self.security_groups = self._get_security_groups()
109
+
110
+ # Create IAM role for instances
111
+ self.instance_role = self._create_instance_role(asg_name)
112
+
113
+ # Create VPC once to be reused by both ECS cluster and ASG
114
+ self._vpc = None # Store VPC for reuse
115
+
116
+ # Create ECS cluster if ECS configuration is detected
117
+ self.ecs_cluster = self._create_ecs_cluster_if_needed()
118
+
119
+ # Create user data (after ECS cluster so it can reference it)
120
+ self.user_data = self._create_user_data()
121
+
122
+ # Create launch template
123
+ self.launch_template = self._create_launch_template(asg_name)
124
+
125
+ # Create Auto Scaling Group
126
+ self.auto_scaling_group = self._create_auto_scaling_group(asg_name)
127
+
128
+ # Add scaling policies
129
+ self._add_scaling_policies()
130
+
131
+ # Add update policy
132
+ self._add_update_policy()
133
+
134
+ # Export SSM parameters
135
+ self._export_ssm_parameters()
136
+
137
+ logger.info(f"Auto Scaling Group {asg_name} built successfully")
138
+
139
+ def _get_ssm_imports(self) -> Dict[str, Any]:
140
+ """Get SSM imports from configuration"""
141
+ return self.asg_config.ssm_imports
142
+
143
+ def _get_security_groups(self) -> List[ec2.ISecurityGroup]:
144
+ """
145
+ Get security groups for the Auto Scaling Group using standardized SSM imports.
146
+
147
+ Returns:
148
+ List of security group references
149
+ """
150
+ security_groups = []
151
+
152
+ # Primary method: Use standardized SSM imports
153
+ ssm_imports = self._get_ssm_imports()
154
+ if "security_group_ids" in ssm_imports:
155
+ imported_sg_ids = ssm_imports["security_group_ids"]
156
+ if isinstance(imported_sg_ids, list):
157
+ for idx, sg_id in enumerate(imported_sg_ids):
158
+ security_groups.append(
159
+ ec2.SecurityGroup.from_security_group_id(
160
+ self, f"SecurityGroup-SSM-{idx}", sg_id
161
+ )
162
+ )
163
+ logger.info(f"Added {len(imported_sg_ids)} security groups from SSM imports")
164
+ else:
165
+ security_groups.append(
166
+ ec2.SecurityGroup.from_security_group_id(
167
+ self, f"SecurityGroup-SSM-0", imported_sg_ids
168
+ )
169
+ )
170
+ logger.info(f"Added security group from SSM imports")
171
+
172
+ # Fallback: Check for direct configuration (backward compatibility)
173
+ elif self.asg_config.security_group_ids:
174
+ logger.warning("Using direct security group configuration - consider migrating to SSM imports")
175
+ for idx, sg_id in enumerate(self.asg_config.security_group_ids):
176
+ logger.info(f"Adding security group from direct config: {sg_id}")
177
+ # Handle comma-separated security group IDs
178
+ if "," in sg_id:
179
+ blocks = sg_id.split(",")
180
+ for block_idx, block in enumerate(blocks):
181
+ security_groups.append(
182
+ ec2.SecurityGroup.from_security_group_id(
183
+ self, f"SecurityGroup-Direct-{idx}-{block_idx}", block.strip()
184
+ )
185
+ )
186
+ else:
187
+ security_groups.append(
188
+ ec2.SecurityGroup.from_security_group_id(
189
+ self, f"SecurityGroup-Direct-{idx}", sg_id
190
+ )
191
+ )
192
+ else:
193
+ logger.warning("No security groups found from SSM imports or direct configuration")
194
+
195
+ return security_groups
196
+
197
+ def _get_vpc_id(self) -> str:
198
+ """
199
+ Get VPC ID using the centralized VPC provider mixin.
200
+ """
201
+ # Use the centralized VPC resolution from VPCProviderMixin
202
+ vpc = self.resolve_vpc(
203
+ config=self.asg_config,
204
+ deployment=self.deployment,
205
+ workload=self.workload
206
+ )
207
+ return vpc.vpc_id
208
+
209
+ def _get_subnet_ids(self) -> List[str]:
210
+ """
211
+ Get subnet IDs using standardized SSM approach.
212
+ """
213
+ # Primary method: Use standardized SSM imports
214
+ ssm_imports = self._get_ssm_imports()
215
+ if "subnet_ids" in ssm_imports:
216
+ return ssm_imports["subnet_ids"]
217
+
218
+ # Fallback: Use VPC provider mixin (backward compatibility)
219
+ elif hasattr(self, '_get_subnets_from_provider'):
220
+ return self._get_subnets_from_provider()
221
+
222
+ # Final fallback: Direct configuration
223
+ elif hasattr(self.asg_config, 'subnet_ids') and self.asg_config.subnet_ids:
224
+ return self.asg_config.subnet_ids
225
+
226
+ else:
227
+ logger.warning("No subnet IDs found, using default behavior")
228
+ return []
229
+
230
+ def _create_instance_role(self, asg_name: str) -> iam.Role:
231
+ """Create IAM role for EC2 instances"""
232
+ role = iam.Role(
233
+ self,
234
+ f"{asg_name}-InstanceRole",
235
+ assumed_by=iam.ServicePrincipal("ec2.amazonaws.com"),
236
+ role_name=f"{asg_name}-role",
237
+ )
238
+
239
+ # Add managed policies
240
+ for policy_name in self.asg_config.managed_policies:
241
+ role.add_managed_policy(
242
+ iam.ManagedPolicy.from_aws_managed_policy_name(policy_name)
243
+ )
244
+
245
+ logger.info(f"Created instance role: {role.role_name}")
246
+ return role
247
+
248
+ def _create_user_data(self) -> ec2.UserData:
249
+ """Create user data for EC2 instances"""
250
+ user_data = ec2.UserData.for_linux()
251
+
252
+ # Add basic setup commands
253
+ user_data.add_commands(
254
+ "#!/bin/bash",
255
+ "yum update -y",
256
+ "yum install -y aws-cfn-bootstrap",
257
+ )
258
+
259
+ # Add user data commands from configuration
260
+ if self.asg_config.user_data_commands:
261
+ # Process template variables in user data commands
262
+ processed_commands = []
263
+ ssm_imports = self._get_ssm_imports()
264
+ for command in self.asg_config.user_data_commands:
265
+ processed_command = command
266
+ # Substitute SSM-imported values
267
+ if "ecs_cluster_name" in ssm_imports and "{{ecs_cluster_name}}" in command:
268
+ cluster_name = ssm_imports["ecs_cluster_name"]
269
+ processed_command = command.replace("{{ecs_cluster_name}}", cluster_name)
270
+ processed_commands.append(processed_command)
271
+
272
+ user_data.add_commands(*processed_commands)
273
+ self.user_data_commands = processed_commands
274
+
275
+ # Add ECS cluster configuration if needed
276
+ if self.ecs_cluster:
277
+ # Use the SSM-imported cluster name if available, otherwise fallback to default format
278
+ ssm_imports = self._get_ssm_imports()
279
+ if "ecs_cluster_name" in ssm_imports:
280
+ cluster_name = ssm_imports["ecs_cluster_name"]
281
+ ecs_commands = [
282
+ f"echo 'ECS_CLUSTER={cluster_name}' >> /etc/ecs/ecs.config",
283
+ "systemctl restart ecs"
284
+ ]
285
+ else:
286
+ # Fallback to default naming pattern
287
+ ecs_commands = [
288
+ "echo 'ECS_CLUSTER={}{}' >> /etc/ecs/ecs.config".format(
289
+ self.deployment.workload_name, self.deployment.environment
290
+ ),
291
+ "systemctl restart ecs"
292
+ ]
293
+ user_data.add_commands(*ecs_commands)
294
+
295
+ logger.info(f"Created user data with {len(self.user_data_commands)} custom commands")
296
+ return user_data
297
+
298
+ def _get_or_create_vpc(self) -> ec2.Vpc:
299
+ """Get or create VPC for reuse across the stack"""
300
+ if self._vpc is None:
301
+ vpc_id = self._get_vpc_id()
302
+ subnet_ids = self._get_subnet_ids()
303
+
304
+ # Create VPC and subnets from imported values
305
+ self._vpc = ec2.Vpc.from_vpc_attributes(
306
+ self, "ImportedVPC",
307
+ vpc_id=vpc_id,
308
+ availability_zones=["us-east-1a", "us-east-1b"] # Add required availability zones
309
+ )
310
+
311
+ # Create and store subnets if we have subnet IDs
312
+ self._subnets = []
313
+ if subnet_ids:
314
+ for i, subnet_id in enumerate(subnet_ids):
315
+ subnet = ec2.Subnet.from_subnet_id(
316
+ self, f"ImportedSubnet-{i}", subnet_id
317
+ )
318
+ self._subnets.append(subnet)
319
+ else:
320
+ # Use default subnets from VPC
321
+ self._subnets = self._vpc.public_subnets
322
+
323
+ return self._vpc
324
+
325
+ def _get_subnets(self) -> List[ec2.Subnet]:
326
+ """Get the subnets from the shared VPC"""
327
+ return getattr(self, '_subnets', [])
328
+
329
+ def _create_ecs_cluster_if_needed(self) -> Optional[ecs.Cluster]:
330
+ """Create ECS cluster if ECS configuration is detected"""
331
+ # Check if user data contains ECS configuration (use raw config since user_data_commands might not be set yet)
332
+ ecs_detected = False
333
+ if self.asg_config.user_data_commands:
334
+ ecs_detected = any("ECS_CLUSTER" in cmd for cmd in self.asg_config.user_data_commands)
335
+
336
+ if ecs_detected:
337
+ ssm_imports = self._get_ssm_imports()
338
+ if "ecs_cluster_name" in ssm_imports:
339
+ cluster_name = ssm_imports["ecs_cluster_name"]
340
+
341
+ # Use the shared VPC
342
+ vpc = self._get_or_create_vpc()
343
+
344
+ self.ecs_cluster = ecs.Cluster.from_cluster_attributes(
345
+ self,
346
+ "ImportedECSCluster",
347
+ cluster_name=cluster_name,
348
+ vpc=vpc
349
+ )
350
+ logger.info(f"Connected to existing ECS cluster: {cluster_name}")
351
+
352
+ return self.ecs_cluster
353
+
354
+ def _create_launch_template(self, asg_name: str) -> ec2.LaunchTemplate:
355
+ """Create launch template for Auto Scaling Group"""
356
+
357
+ # Use the configured AMI ID or fall back to appropriate lookup
358
+ if self.asg_config.ami_id:
359
+ # Use explicit AMI ID provided by user
360
+ machine_image = ec2.MachineImage.lookup(name=self.asg_config.ami_id)
361
+ elif self.asg_config.ami_type:
362
+ # Use AMI type for dynamic lookup
363
+ if self.asg_config.ami_type.upper() == "AMAZON-LINUX-2023":
364
+ machine_image = ec2.MachineImage.latest_amazon_linux2023()
365
+ elif self.asg_config.ami_type.upper() == "AMAZON-LINUX-2022":
366
+ machine_image = ec2.MachineImage.latest_amazon_linux2022()
367
+ elif self.asg_config.ami_type.upper() == "AMAZON-LINUX-2":
368
+ machine_image = ec2.MachineImage.latest_amazon_linux2()
369
+ elif self.asg_config.ami_type.upper() == "ECS_OPTIMIZED":
370
+ machine_image = ec2.MachineImage.latest_amazon_linux2023()
371
+ else:
372
+ # Default to latest Amazon Linux
373
+ machine_image = ec2.MachineImage.latest_amazon_linux2023()
374
+ else:
375
+ # Default fallback
376
+ machine_image = ec2.MachineImage.latest_amazon_linux2023()
377
+
378
+ launch_template = ec2.LaunchTemplate(
379
+ self,
380
+ f"{asg_name}-LaunchTemplate",
381
+ instance_type=ec2.InstanceType(self.asg_config.instance_type),
382
+ machine_image=machine_image,
383
+ role=self.instance_role,
384
+ user_data=self.user_data,
385
+ security_group=self.security_groups[0] if self.security_groups else None,
386
+ key_name=self.asg_config.key_name,
387
+ detailed_monitoring=self.asg_config.detailed_monitoring,
388
+ block_devices=[
389
+ ec2.BlockDevice(
390
+ device_name=block_device.get("device_name", "/dev/xvda"),
391
+ volume=ec2.BlockDeviceVolume.ebs(
392
+ volume_size=block_device.get("volume_size", 8),
393
+ volume_type=getattr(ec2.EbsDeviceVolumeType, block_device.get("volume_type", "GP3").upper()),
394
+ delete_on_termination=block_device.get("delete_on_termination", True),
395
+ encrypted=block_device.get("encrypted", False),
396
+ )
397
+ ) for block_device in self.asg_config.block_devices
398
+ ] if self.asg_config.block_devices else None,
399
+ )
400
+
401
+ logger.info(f"Created launch template: {launch_template.launch_template_name}")
402
+ return launch_template
403
+
404
+ def _create_auto_scaling_group(self, asg_name: str) -> autoscaling.AutoScalingGroup:
405
+ """Create Auto Scaling Group"""
406
+ # Use the shared VPC and subnets
407
+ vpc = self._get_or_create_vpc()
408
+ subnets = self._get_subnets()
409
+
410
+ auto_scaling_group = autoscaling.AutoScalingGroup(
411
+ self,
412
+ f"{asg_name}-ASG",
413
+ vpc=vpc,
414
+ vpc_subnets=ec2.SubnetSelection(subnets=subnets),
415
+ launch_template=self.launch_template,
416
+ min_capacity=self.asg_config.min_capacity,
417
+ max_capacity=self.asg_config.max_capacity,
418
+ desired_capacity=self.asg_config.desired_capacity,
419
+ health_check=autoscaling.HealthCheck.elb(
420
+ grace=cdk.Duration.seconds(self.asg_config.health_check_grace_period)
421
+ ) if self.asg_config.health_check_type.upper() == "ELB" else autoscaling.HealthCheck.ec2(
422
+ grace=cdk.Duration.seconds(self.asg_config.health_check_grace_period)
423
+ ),
424
+ cooldown=cdk.Duration.seconds(self.asg_config.cooldown),
425
+ termination_policies=[
426
+ getattr(autoscaling.TerminationPolicy, policy.upper())
427
+ for policy in self.asg_config.termination_policies
428
+ ],
429
+ )
430
+
431
+ # Attach target groups if configured
432
+ self._attach_target_groups(auto_scaling_group)
433
+
434
+ logger.info(f"Created Auto Scaling Group: {asg_name}")
435
+ return auto_scaling_group
436
+
437
+ def _attach_target_groups(self, asg: autoscaling.AutoScalingGroup) -> None:
438
+ """Attach the Auto Scaling Group to target groups"""
439
+ target_group_arns = self._get_target_group_arns()
440
+
441
+ if not target_group_arns:
442
+ logger.warning("No target group ARNs found for Auto Scaling Group")
443
+ return
444
+
445
+ # Get the underlying CloudFormation resource to add target group ARNs
446
+ cfn_asg = asg.node.default_child
447
+ cfn_asg.add_property_override("TargetGroupARNs", target_group_arns)
448
+
449
+ def _get_target_group_arns(self) -> List[str]:
450
+ """Get target group ARNs using standardized SSM approach"""
451
+ target_group_arns = []
452
+
453
+ # Use standardized SSM imports
454
+ ssm_imports = self._get_ssm_imports()
455
+ if "target_group_arns" in ssm_imports:
456
+ imported_arns = ssm_imports["target_group_arns"]
457
+ if isinstance(imported_arns, list):
458
+ target_group_arns.extend(imported_arns)
459
+ else:
460
+ target_group_arns.append(imported_arns)
461
+
462
+ # Fallback: Direct configuration
463
+ elif self.asg_config.target_group_arns:
464
+ target_group_arns.extend(self.asg_config.target_group_arns)
465
+
466
+ return target_group_arns
467
+
468
+ def _add_scaling_policies(self) -> None:
469
+ """Add scaling policies to the Auto Scaling Group"""
470
+ if not self.asg_config.scaling_policies:
471
+ return
472
+
473
+ for policy_config in self.asg_config.scaling_policies:
474
+ if policy_config.get("type") == "target_tracking":
475
+ # Create a target tracking scaling policy for CPU utilization
476
+ scaling_policy = autoscaling.CfnScalingPolicy(
477
+ self,
478
+ "CPUScalingPolicy",
479
+ auto_scaling_group_name=self.auto_scaling_group.auto_scaling_group_name,
480
+ policy_type="TargetTrackingScaling",
481
+ target_tracking_configuration=autoscaling.CfnScalingPolicy.TargetTrackingConfigurationProperty(
482
+ target_value=policy_config.get("target_cpu", 70),
483
+ predefined_metric_specification=autoscaling.CfnScalingPolicy.PredefinedMetricSpecificationProperty(
484
+ predefined_metric_type="ASGAverageCPUUtilization"
485
+ )
486
+ )
487
+ )
488
+ logger.info("Added CPU utilization scaling policy")
489
+
490
+ def _add_update_policy(self) -> None:
491
+ """Add update policy to the Auto Scaling Group"""
492
+ update_policy = self.asg_config.update_policy
493
+
494
+ if not update_policy:
495
+ return
496
+
497
+ # Get the underlying CloudFormation resource to add update policy
498
+ cfn_asg = self.auto_scaling_group.node.default_child
499
+
500
+ # Clear any existing update policy and set the rolling update policy
501
+ cfn_asg.add_property_override("UpdatePolicy", {
502
+ "AutoScalingRollingUpdate": {
503
+ "MinInstancesInService": update_policy.get("min_instances_in_service", 1),
504
+ "MaxBatchSize": update_policy.get("max_batch_size", 1),
505
+ "PauseTime": f"PT{update_policy.get('pause_time', 300)}S"
506
+ }
507
+ })
508
+
509
+ logger.info("Added rolling update policy to Auto Scaling Group")
510
+
511
+ def _export_ssm_parameters(self) -> None:
512
+ """Export SSM parameters using standardized approach"""
513
+ if not self.auto_scaling_group:
514
+ logger.warning("No Auto Scaling Group to export")
515
+ return
516
+
517
+ # Prepare resource values for export
518
+ resource_values = {
519
+ "auto_scaling_group_name": self.auto_scaling_group.auto_scaling_group_name,
520
+ "auto_scaling_group_arn": self.auto_scaling_group.auto_scaling_group_arn,
521
+ }
522
+
523
+ # Export using standardized SSM mixin
524
+ exported_params = self.export_standardized_ssm_parameters(resource_values)
525
+
526
+ logger.info(f"Exported SSM parameters: {exported_params}")
527
+
528
+
529
+ # Backward compatibility alias
530
+ AutoScalingStackStandardized = AutoScalingStack
@@ -15,7 +15,7 @@ from cdk_factory.configurations.deployment import DeploymentConfig
15
15
  from cdk_factory.configurations.stack import StackConfig
16
16
  from cdk_factory.configurations.resources.code_artifact import CodeArtifactConfig
17
17
  from cdk_factory.interfaces.istack import IStack
18
- from cdk_factory.interfaces.enhanced_ssm_parameter_mixin import EnhancedSsmParameterMixin
18
+ from cdk_factory.interfaces.standardized_ssm_mixin import StandardizedSsmMixin
19
19
  from cdk_factory.stack.stack_module_registry import register_stack
20
20
  from cdk_factory.workload.workload_factory import WorkloadConfig
21
21
 
@@ -24,7 +24,7 @@ logger = Logger(service="CodeArtifactStack")
24
24
 
25
25
  @register_stack("code_artifact_library_module")
26
26
  @register_stack("code_artifact_stack")
27
- class CodeArtifactStack(IStack, EnhancedSsmParameterMixin):
27
+ class CodeArtifactStack(IStack, StandardizedSsmMixin):
28
28
  """
29
29
  Reusable stack for AWS CodeArtifact.
30
30
  Supports creating domains and repositories with configurable settings.