in packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts [943:1093]
constructor(scope: Construct, id: string, props: AutoScalingGroupProps) {
super(scope, id, {
physicalName: props.autoScalingGroupName,
});
this.newInstancesProtectedFromScaleIn = props.newInstancesProtectedFromScaleIn;
if (props.initOptions && !props.init) {
throw new Error('Setting \'initOptions\' requires that \'init\' is also set');
}
this.securityGroup = props.securityGroup || new ec2.SecurityGroup(this, 'InstanceSecurityGroup', {
vpc: props.vpc,
allowAllOutbound: props.allowAllOutbound !== false,
});
this.connections = new ec2.Connections({ securityGroups: [this.securityGroup] });
this.securityGroups.push(this.securityGroup);
Tags.of(this).add(NAME_TAG, this.node.path);
this.role = props.role || new iam.Role(this, 'InstanceRole', {
roleName: PhysicalName.GENERATE_IF_NEEDED,
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
});
this.grantPrincipal = this.role;
if (props.groupMetrics) {
this.groupMetrics.push(...props.groupMetrics);
}
const iamProfile = new iam.CfnInstanceProfile(this, 'InstanceProfile', {
roles: [this.role.roleName],
});
// use delayed evaluation
const imageConfig = props.machineImage.getImage(this);
this.userData = props.userData ?? imageConfig.userData;
const userDataToken = Lazy.string({ produce: () => Fn.base64(this.userData.render()) });
const securityGroupsToken = Lazy.list({ produce: () => this.securityGroups.map(sg => sg.securityGroupId) });
const launchConfig = new CfnLaunchConfiguration(this, 'LaunchConfig', {
imageId: imageConfig.imageId,
keyName: props.keyName,
instanceType: props.instanceType.toString(),
instanceMonitoring: (props.instanceMonitoring !== undefined ? (props.instanceMonitoring === Monitoring.DETAILED) : undefined),
securityGroups: securityGroupsToken,
iamInstanceProfile: iamProfile.ref,
userData: userDataToken,
associatePublicIpAddress: props.associatePublicIpAddress,
spotPrice: props.spotPrice,
blockDeviceMappings: (props.blockDevices !== undefined ?
synthesizeBlockDeviceMappings(this, props.blockDevices) : undefined),
});
launchConfig.node.addDependency(this.role);
// desiredCapacity just reflects what the user has supplied.
const desiredCapacity = props.desiredCapacity;
const minCapacity = props.minCapacity ?? 1;
const maxCapacity = props.maxCapacity ?? desiredCapacity ?? Math.max(minCapacity, 1);
withResolved(minCapacity, maxCapacity, (min, max) => {
if (min > max) {
throw new Error(`minCapacity (${min}) should be <= maxCapacity (${max})`);
}
});
withResolved(desiredCapacity, minCapacity, (desired, min) => {
if (desired === undefined) { return; }
if (desired < min) {
throw new Error(`Should have minCapacity (${min}) <= desiredCapacity (${desired})`);
}
});
withResolved(desiredCapacity, maxCapacity, (desired, max) => {
if (desired === undefined) { return; }
if (max < desired) {
throw new Error(`Should have desiredCapacity (${desired}) <= maxCapacity (${max})`);
}
});
if (desiredCapacity !== undefined) {
Annotations.of(this).addWarning('desiredCapacity has been configured. Be aware this will reset the size of your AutoScalingGroup on every deployment. See https://github.com/aws/aws-cdk/issues/5215');
}
this.maxInstanceLifetime = props.maxInstanceLifetime;
if (this.maxInstanceLifetime &&
(this.maxInstanceLifetime.toSeconds() < 604800 || this.maxInstanceLifetime.toSeconds() > 31536000)) {
throw new Error('maxInstanceLifetime must be between 7 and 365 days (inclusive)');
}
if (props.notificationsTopic && props.notifications) {
throw new Error('Cannot set \'notificationsTopic\' and \'notifications\', \'notificationsTopic\' is deprecated use \'notifications\' instead');
}
if (props.notificationsTopic) {
this.notifications = [{
topic: props.notificationsTopic,
}];
}
if (props.notifications) {
this.notifications = props.notifications.map(nc => ({
topic: nc.topic,
scalingEvents: nc.scalingEvents ?? ScalingEvents.ALL,
}));
}
const { subnetIds, hasPublic } = props.vpc.selectSubnets(props.vpcSubnets);
const asgProps: CfnAutoScalingGroupProps = {
autoScalingGroupName: this.physicalName,
cooldown: props.cooldown?.toSeconds().toString(),
minSize: Tokenization.stringifyNumber(minCapacity),
maxSize: Tokenization.stringifyNumber(maxCapacity),
desiredCapacity: desiredCapacity !== undefined ? Tokenization.stringifyNumber(desiredCapacity) : undefined,
launchConfigurationName: launchConfig.ref,
loadBalancerNames: Lazy.list({ produce: () => this.loadBalancerNames }, { omitEmpty: true }),
targetGroupArns: Lazy.list({ produce: () => this.targetGroupArns }, { omitEmpty: true }),
notificationConfigurations: this.renderNotificationConfiguration(),
metricsCollection: Lazy.any({ produce: () => this.renderMetricsCollection() }),
vpcZoneIdentifier: subnetIds,
healthCheckType: props.healthCheck && props.healthCheck.type,
healthCheckGracePeriod: props.healthCheck && props.healthCheck.gracePeriod && props.healthCheck.gracePeriod.toSeconds(),
maxInstanceLifetime: this.maxInstanceLifetime ? this.maxInstanceLifetime.toSeconds() : undefined,
newInstancesProtectedFromScaleIn: Lazy.any({ produce: () => this.newInstancesProtectedFromScaleIn }),
terminationPolicies: props.terminationPolicies,
};
if (!hasPublic && props.associatePublicIpAddress) {
throw new Error("To set 'associatePublicIpAddress: true' you must select Public subnets (vpcSubnets: { subnetType: SubnetType.PUBLIC })");
}
this.autoScalingGroup = new CfnAutoScalingGroup(this, 'ASG', asgProps);
this.osType = imageConfig.osType;
this.autoScalingGroupName = this.getResourceNameAttribute(this.autoScalingGroup.ref),
this.autoScalingGroupArn = Stack.of(this).formatArn({
service: 'autoscaling',
resource: 'autoScalingGroup:*:autoScalingGroupName',
resourceName: this.autoScalingGroupName,
});
this.node.defaultChild = this.autoScalingGroup;
this.applyUpdatePolicies(props, { desiredCapacity, minCapacity });
if (props.init) {
this.applyCloudFormationInit(props.init, props.initOptions);
}
this.spotPrice = props.spotPrice;
if (props.requireImdsv2) {
Aspects.of(this).add(new AutoScalingGroupRequireImdsv2Aspect());
}
}