diff --git a/packages/aws-cdk-lib/aws-autoscaling/README.md b/packages/aws-cdk-lib/aws-autoscaling/README.md index 8fc9c48003826..04c09fcca3fb8 100644 --- a/packages/aws-cdk-lib/aws-autoscaling/README.md +++ b/packages/aws-cdk-lib/aws-autoscaling/README.md @@ -290,7 +290,7 @@ autoScalingGroup.scaleOnSchedule('AllowDownscalingAtNight', { ### Instance Maintenance Policy -The `instanceMaintenancePolicy` allows you to configure an instance maintenance policy for +The Instance Maintenance Policy allows you to configure an instance maintenance policy for your Auto Scaling group to meet specific capacity requirements during events that cause instances to be replaced, such as an instance refresh or the health check process. @@ -300,9 +300,9 @@ when health checks indicate an impaired instance. With an instance maintenance p can make sure that Amazon EC2 Auto Scaling first launches a new instance and then waits for it to be fully ready before terminating the unhealthy instance. -An instance maintenance policy also helps you minimize any potential disruptions in cases -where multiple instances are replaced at the same time. You set the `minHealthyPercentage` -and the `maxHealthyPercentage` for the policy, and your Auto Scaling group can only +An instance maintenance policy also helps you minimize any potential disruptions in cases where +multiple instances are replaced at the same time. You set the `maintenancePolicyMinHealthPercentage` +and the `maintenancePolicyMaxHealthPercentage` for the policy, and your Auto Scaling group can only increase and decrease capacity within that minimum-maximum range when replacing instances. A larger range increases the number of instances that can be replaced at the same time. @@ -313,10 +313,8 @@ new autoscaling.AutoScalingGroup(this, 'ASG', { vpc, instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: 200, - minHealthyPercentage: 100, - }, + maintenancePolicyMaxHealthPercentage: 200, + maintenancePolicyMinHealthPercentage: 100, }); ``` diff --git a/packages/aws-cdk-lib/aws-autoscaling/lib/auto-scaling-group.ts b/packages/aws-cdk-lib/aws-autoscaling/lib/auto-scaling-group.ts index 6454ac0c0a884..53020763fa280 100644 --- a/packages/aws-cdk-lib/aws-autoscaling/lib/auto-scaling-group.ts +++ b/packages/aws-cdk-lib/aws-autoscaling/lib/auto-scaling-group.ts @@ -575,41 +575,6 @@ export interface LaunchTemplateOverrides { readonly weightedCapacity?: number } -/** - * Allows you to configure an instance maintenance policy for your Auto Scaling group to - * meet specific capacity requirements during events that cause instances to be replaced, such as an instance - * refresh or the health check process. - * - * @see https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-instance-maintenance-policy.html - */ -export interface InstanceMaintenancePolicy { - /** - * Specifies the upper threshold as a percentage of the desired capacity of the Auto Scaling group. - * It represents the maximum percentage of the group that can be in service and healthy, or pending, - * to support your workload when replacing instances. - * - * Value range is 100 to 200. After it's set, a value of -1 will clear the previously set value. - * - * Both `minHealthyPercentage` and `maxHealthyPercentage` must be specified, and the difference between - * them cannot be greater than 100. A large range increases the number of instances that can be - * replaced at the same time. - */ - readonly maxHealthyPercentage: number; - - /** - * Specifies the lower threshold as a percentage of the desired capacity of the Auto Scaling group. - * It represents the minimum percentage of the group to keep in service, healthy, and ready to use - * to support your workload when replacing instances. - * - * Value range is 0 to 100. After it's set, a value of -1 will clear the previously set value. - * - * Both `minHealthyPercentage` and `maxHealthyPercentage` must be specified, and the difference between - * them cannot be greater than 100. A large range increases the number of instances that can be - * replaced at the same time. - */ - readonly minHealthyPercentage: number; -} - /** * Properties of a Fleet */ @@ -723,11 +688,40 @@ export interface AutoScalingGroupProps extends CommonAutoScalingGroupProps { readonly requireImdsv2?: boolean; /** - * An instance maintenance policy. + * Specifies the upper threshold as a percentage of the desired capacity of the Auto Scaling group. + * It represents the maximum percentage of the group that can be in service and healthy, or pending, + * to support your workload when replacing instances. + * + * Value range is 0 to 100. After it's set, both `maintenancePolicyMinHealthPercentage` and + * `maintenancePolicyMaxHealthPercentage` to -1 will clear the previously set value. + * + * Both or neither of `maintenancePolicyMinHealthPercentage` and `maintenancePolicyMaxHealthPercentage` + * must be specified, and the difference between them cannot be greater than 100. A large range increases + * the number of instances that can be replaced at the same time. + * + * @see https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-instance-maintenance-policy.html + * + * @default - Do not provide percentage. + */ + readonly maintenancePolicyMaxHealthPercentage?: number; + + /** + * Specifies the lower threshold as a percentage of the desired capacity of the Auto Scaling group. + * It represents the minimum percentage of the group to keep in service, healthy, and ready to use + * to support your workload when replacing instances. + * + * Value range is 0 to 100. After it's set, both `maintenancePolicyMinHealthPercentage` and + * `maintenancePolicyMaxHealthPercentage` to -1 will clear the previously set value. + * + * Both or neither of `maintenancePolicyMinHealthPercentage` and `maintenancePolicyMaxHealthPercentage` + * must be specified, and the difference between them cannot be greater than 100. A large range increases + * the number of instances that can be replaced at the same time. * - * @default - no instance maintenance policy. + * @see https://docs.aws.amazon.com/autoscaling/ec2/userguide/ec2-auto-scaling-instance-maintenance-policy.html + * + * @default - Do not provide percentage. */ - readonly instanceMaintenancePolicy?: InstanceMaintenancePolicy; + readonly maintenancePolicyMinHealthPercentage?: number; } /** @@ -1450,8 +1444,6 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements })); } - this.validateInstanceMaintenancePolicy(props.instanceMaintenancePolicy); - const { subnetIds, hasPublic } = props.vpc.selectSubnets(props.vpcSubnets); const asgProps: CfnAutoScalingGroupProps = { autoScalingGroupName: this.physicalName, @@ -1471,7 +1463,10 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements terminationPolicies: props.terminationPolicies, defaultInstanceWarmup: props.defaultInstanceWarmup?.toSeconds(), capacityRebalance: props.capacityRebalance, - instanceMaintenancePolicy: props.instanceMaintenancePolicy, + instanceMaintenancePolicy: this.renderInstanceMaintenancePolicy( + props.maintenancePolicyMaxHealthPercentage, + props.maintenancePolicyMinHealthPercentage, + ), ...this.getLaunchSettings(launchConfig, props.launchTemplate ?? launchTemplateFromConfig, props.mixedInstancesPolicy), }; @@ -1886,26 +1881,43 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements return errors; } - private validateInstanceMaintenancePolicy(policy?: InstanceMaintenancePolicy) { - if (!policy) { + private renderInstanceMaintenancePolicy( + maxHealthPercentage?: number, + minHealthPercentage?: number, + ): CfnAutoScalingGroup.InstanceMaintenancePolicyProperty | undefined { + if (maxHealthPercentage === undefined && minHealthPercentage === undefined) { return; } - const maxHealthyPercentage = policy.maxHealthyPercentage; - const minHealthyPercentage = policy.minHealthyPercentage; - - if (maxHealthyPercentage !== -1 && (maxHealthyPercentage < 100 || maxHealthyPercentage > 200)) { - throw new Error(`maxHealthyPercentage must be between 100 and 200, or -1 to clear the previously set value, got ${maxHealthyPercentage}`); + if ( + (maxHealthPercentage !== undefined && minHealthPercentage === undefined) + || (maxHealthPercentage === undefined && minHealthPercentage !== undefined) + ) { + throw new Error(`Both or neither of maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage must be specified, got maintenancePolicyMinHealthPercentage: ${minHealthPercentage} and maintenancePolicyMaxHealthPercentage: ${maxHealthPercentage}`); + } + if ((maxHealthPercentage !== -1 && minHealthPercentage === -1) || (maxHealthPercentage === -1 && minHealthPercentage !== -1)) { + throw new Error(`Both maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage must be -1 to clear the previously set value, got maintenancePolicyMinHealthPercentage: ${minHealthPercentage} and maintenancePolicyMaxHealthPercentage: ${maxHealthPercentage}`); } - if (minHealthyPercentage !== -1 && (minHealthyPercentage < 0 || minHealthyPercentage > 100)) { - throw new Error(`minHealthyPercentage must be between 0 and 100, or -1 to clear the previously set value, got ${minHealthyPercentage}`); + if (maxHealthPercentage !== undefined && maxHealthPercentage !== -1 && (maxHealthPercentage < 100 || maxHealthPercentage > 200)) { + throw new Error(`maintenancePolicyMaxHealthPercentage must be between 100 and 200, or -1 to clear the previously set value, got ${maxHealthPercentage}`); } - if ((maxHealthyPercentage !== -1 && minHealthyPercentage === -1) || (maxHealthyPercentage === -1 && minHealthyPercentage !== -1)) { - throw new Error(`Both minHealthyPercentage and maxHealthyPercentage must be -1 to clear the previously set value, got minHealthyPercentage: ${minHealthyPercentage} and maxHealthyPercentage: ${maxHealthyPercentage}`); + if (minHealthPercentage !== undefined && minHealthPercentage !== -1 && (minHealthPercentage < 0 || minHealthPercentage > 100)) { + throw new Error(`maintenancePolicyMinHealthPercentage must be between 0 and 100, or -1 to clear the previously set value, got ${minHealthPercentage}`); } - if (maxHealthyPercentage !== -1 && minHealthyPercentage !== -1 && maxHealthyPercentage - minHealthyPercentage > 100) { - throw new Error(`The difference between minHealthyPercentage and maxHealthyPercentage cannot be greater than 100, got ${maxHealthyPercentage - minHealthyPercentage}`); + if ( + maxHealthPercentage !== undefined + && minHealthPercentage !== undefined + && maxHealthPercentage !== -1 + && minHealthPercentage !== -1 + && maxHealthPercentage - minHealthPercentage > 100 + ) { + throw new Error(`The difference between maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage cannot be greater than 100, got ${maxHealthPercentage - minHealthPercentage}`); } + + return { + maxHealthyPercentage: maxHealthPercentage, + minHealthyPercentage: minHealthPercentage, + }; } } diff --git a/packages/aws-cdk-lib/aws-autoscaling/test/auto-scaling-group.test.ts b/packages/aws-cdk-lib/aws-autoscaling/test/auto-scaling-group.test.ts index fd0276a267d2c..9c8e919fa3f77 100644 --- a/packages/aws-cdk-lib/aws-autoscaling/test/auto-scaling-group.test.ts +++ b/packages/aws-cdk-lib/aws-autoscaling/test/auto-scaling-group.test.ts @@ -2404,7 +2404,7 @@ test('requires imdsv2 when @aws-cdk/aws-autoscaling:generateLaunchTemplateInstea }); describe('InstanceMaintenancePolicy', () => { - test('InstanceMaintenancePolicy can be specified', () => { + test('maintenancePolicyMaxHealthPercentage and maintenancePolicyMinHealthPercentage can be specified', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2412,10 +2412,8 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: 200, - minHealthyPercentage: 100, - }, + maintenancePolicyMaxHealthPercentage: 200, + maintenancePolicyMinHealthPercentage: 100, }); // Then @@ -2427,7 +2425,7 @@ describe('InstanceMaintenancePolicy', () => { }); }); - test('maxHealthyPercentage and minHealthyPercentage can be set to -1', () => { + test('maintenancePolicyMaxHealthPercentage and maintenancePolicyMinHealthPercentage can be set to -1', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2435,10 +2433,8 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: -1, - minHealthyPercentage: -1, - }, + maintenancePolicyMaxHealthPercentage: -1, + maintenancePolicyMinHealthPercentage: -1, }); // Then @@ -2450,7 +2446,7 @@ describe('InstanceMaintenancePolicy', () => { }); }); - test('throws if maxHealthyPercentage is greater than 200', () => { + test('throws if maintenancePolicyMaxHealthPercentage is greater than 200', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2461,15 +2457,13 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: 250, - minHealthyPercentage: 100, - }, + maintenancePolicyMaxHealthPercentage: 250, + maintenancePolicyMinHealthPercentage: 100, }); - }).toThrow(/maxHealthyPercentage must be between 100 and 200, or -1 to clear the previously set value, got 250/); + }).toThrow(/maintenancePolicyMaxHealthPercentage must be between 100 and 200, or -1 to clear the previously set value, got 250/); }); - test('throws if maxHealthyPercentage is less than 100', () => { + test('throws if maintenancePolicyMaxHealthPercentage is less than 100', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2480,15 +2474,13 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: 50, - minHealthyPercentage: 100, - }, + maintenancePolicyMaxHealthPercentage: 50, + maintenancePolicyMinHealthPercentage: 100, }); - }).toThrow(/maxHealthyPercentage must be between 100 and 200, or -1 to clear the previously set value, got 50/); + }).toThrow(/maintenancePolicyMaxHealthPercentage must be between 100 and 200, or -1 to clear the previously set value, got 50/); }); - test('throws if minHealthyPercentage is greater than 100', () => { + test('throws if maintenancePolicyMinHealthPercentage is greater than 100', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2499,15 +2491,13 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: 200, - minHealthyPercentage: 150, - }, + maintenancePolicyMaxHealthPercentage: 200, + maintenancePolicyMinHealthPercentage: 150, }); - }).toThrow(/minHealthyPercentage must be between 0 and 100, or -1 to clear the previously set value, got 150/); + }).toThrow(/maintenancePolicyMinHealthPercentage must be between 0 and 100, or -1 to clear the previously set value, got 150/); }); - test('throws if minHealthyPercentage is less than 0', () => { + test('throws if maintenancePolicyMinHealthPercentage is less than 0', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2518,15 +2508,13 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: 200, - minHealthyPercentage: -100, - }, + maintenancePolicyMaxHealthPercentage: 200, + maintenancePolicyMinHealthPercentage: -100, }); - }).toThrow(/minHealthyPercentage must be between 0 and 100, or -1 to clear the previously set value, got -100/); + }).toThrow(/maintenancePolicyMinHealthPercentage must be between 0 and 100, or -1 to clear the previously set value, got -100/); }); - test('throws if only minHealthyPercentage is set to -1', () => { + test('throws if only maintenancePolicyMinHealthPercentage is set to -1', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2537,15 +2525,13 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: 200, - minHealthyPercentage: -1, - }, + maintenancePolicyMaxHealthPercentage: 200, + maintenancePolicyMinHealthPercentage: -1, }); - }).toThrow(/Both minHealthyPercentage and maxHealthyPercentage must be -1 to clear the previously set value, got minHealthyPercentage: -1 and maxHealthyPercentage: 200/); + }).toThrow(/Both maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage must be -1 to clear the previously set value, got maintenancePolicyMinHealthPercentage: -1 and maintenancePolicyMaxHealthPercentage: 200/); }); - test('throws if only maxHealthyPercentage is set to -1', () => { + test('throws if only maintenancePolicyMaxHealthPercentage is set to -1', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2556,15 +2542,13 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: -1, - minHealthyPercentage: 100, - }, + maintenancePolicyMaxHealthPercentage: -1, + maintenancePolicyMinHealthPercentage: 100, }); - }).toThrow(/Both minHealthyPercentage and maxHealthyPercentage must be -1 to clear the previously set value, got minHealthyPercentage: 100 and maxHealthyPercentage: -1/); + }).toThrow(/Both maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage must be -1 to clear the previously set value, got maintenancePolicyMinHealthPercentage: 100 and maintenancePolicyMaxHealthPercentage: -1/); }); - test('throws if a difference between minHealthyPercentage and maxHealthyPercentage is greater than 100', () => { + test('throws if only maintenancePolicyMinHealthPercentage is specified', () => { // GIVEN const stack = new cdk.Stack(); const vpc = mockVpc(stack); @@ -2575,12 +2559,42 @@ describe('InstanceMaintenancePolicy', () => { vpc, instanceType: new ec2.InstanceType('t2.micro'), machineImage: ec2.MachineImage.latestAmazonLinux2(), - instanceMaintenancePolicy: { - maxHealthyPercentage: 200, - minHealthyPercentage: 0, - }, + maintenancePolicyMinHealthPercentage: 100, + }); + }).toThrow(/Both or neither of maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage must be specified, got maintenancePolicyMinHealthPercentage: 100 and maintenancePolicyMaxHealthPercentage: undefined/); + }); + + test('throws if only maintenancePolicyMaxHealthPercentage is specified', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = mockVpc(stack); + + // Then + expect(() => { + new autoscaling.AutoScalingGroup(stack, 'ASG', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ec2.MachineImage.latestAmazonLinux2(), + maintenancePolicyMaxHealthPercentage: 200, + }); + }).toThrow(/Both or neither of maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage must be specified, got maintenancePolicyMinHealthPercentage: undefined and maintenancePolicyMaxHealthPercentage: 200/); + }); + + test('throws if a difference between maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage is greater than 100', () => { + // GIVEN + const stack = new cdk.Stack(); + const vpc = mockVpc(stack); + + // Then + expect(() => { + new autoscaling.AutoScalingGroup(stack, 'ASG', { + vpc, + instanceType: new ec2.InstanceType('t2.micro'), + machineImage: ec2.MachineImage.latestAmazonLinux2(), + maintenancePolicyMaxHealthPercentage: 200, + maintenancePolicyMinHealthPercentage: 0, }); - }).toThrow(/The difference between minHealthyPercentage and maxHealthyPercentage cannot be greater than 100, got 200/); + }).toThrow(/The difference between maintenancePolicyMinHealthPercentage and maintenancePolicyMaxHealthPercentage cannot be greater than 100, got 200/); }); });