in packages/@aws-cdk/aws-ec2/lib/launch-template.ts [485:653]
constructor(scope: Construct, id: string, props: LaunchTemplateProps = {}) {
super(scope, id);
// Basic validation of the provided spot block duration
const spotDuration = props?.spotOptions?.blockDuration?.toHours({ integral: true });
if (spotDuration !== undefined && (spotDuration < 1 || spotDuration > 6)) {
// See: https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/spot-requests.html#fixed-duration-spot-instances
Annotations.of(this).addError('Spot block duration must be exactly 1, 2, 3, 4, 5, or 6 hours.');
}
this.role = props.role;
this._grantPrincipal = this.role;
const iamProfile: iam.CfnInstanceProfile | undefined = this.role ? new iam.CfnInstanceProfile(this, 'Profile', {
roles: [this.role!.roleName],
}) : undefined;
if (props.securityGroup) {
this._connections = new Connections({ securityGroups: [props.securityGroup] });
}
const securityGroupsToken = Lazy.list({
produce: () => {
if (this._connections && this._connections.securityGroups.length > 0) {
return this._connections.securityGroups.map(sg => sg.securityGroupId);
}
return undefined;
},
});
if (props.userData) {
this.userData = props.userData;
}
const userDataToken = Lazy.string({
produce: () => {
if (this.userData) {
return Fn.base64(this.userData.render());
}
return undefined;
},
});
const imageConfig: MachineImageConfig | undefined = props.machineImage?.getImage(this);
if (imageConfig) {
this.osType = imageConfig.osType;
}
let marketOptions: any = undefined;
if (props?.spotOptions) {
marketOptions = {
marketType: 'spot',
spotOptions: {
blockDurationMinutes: spotDuration !== undefined ? spotDuration * 60 : undefined,
instanceInterruptionBehavior: props.spotOptions.interruptionBehavior,
maxPrice: props.spotOptions.maxPrice?.toString(),
spotInstanceType: props.spotOptions.requestType,
validUntil: props.spotOptions.validUntil?.date.toUTCString(),
},
};
// Remove SpotOptions if there are none.
if (Object.keys(marketOptions.spotOptions).filter(k => marketOptions.spotOptions[k]).length == 0) {
marketOptions.spotOptions = undefined;
}
}
this.tags = new TagManager(TagType.KEY_VALUE, 'AWS::EC2::LaunchTemplate');
const tagsToken = Lazy.any({
produce: () => {
if (this.tags.hasTags()) {
const renderedTags = this.tags.renderTags();
const lowerCaseRenderedTags = renderedTags.map( (tag: { [key: string]: string}) => {
return {
key: tag.Key,
value: tag.Value,
};
});
return [
{
resourceType: 'instance',
tags: lowerCaseRenderedTags,
},
{
resourceType: 'volume',
tags: lowerCaseRenderedTags,
},
];
}
return undefined;
},
});
const resource = new CfnLaunchTemplate(this, 'Resource', {
launchTemplateName: props?.launchTemplateName,
launchTemplateData: {
blockDeviceMappings: props?.blockDevices !== undefined ? launchTemplateBlockDeviceMappings(this, props.blockDevices) : undefined,
creditSpecification: props?.cpuCredits !== undefined ? {
cpuCredits: props.cpuCredits,
} : undefined,
disableApiTermination: props?.disableApiTermination,
ebsOptimized: props?.ebsOptimized,
enclaveOptions: props?.nitroEnclaveEnabled !== undefined ? {
enabled: props.nitroEnclaveEnabled,
} : undefined,
hibernationOptions: props?.hibernationConfigured !== undefined ? {
configured: props.hibernationConfigured,
} : undefined,
iamInstanceProfile: iamProfile !== undefined ? {
arn: iamProfile.getAtt('Arn').toString(),
} : undefined,
imageId: imageConfig?.imageId,
instanceType: props?.instanceType?.toString(),
instanceInitiatedShutdownBehavior: props?.instanceInitiatedShutdownBehavior,
instanceMarketOptions: marketOptions,
keyName: props?.keyName,
monitoring: props?.detailedMonitoring !== undefined ? {
enabled: props.detailedMonitoring,
} : undefined,
securityGroupIds: securityGroupsToken,
tagSpecifications: tagsToken,
userData: userDataToken,
// Fields not yet implemented:
// ==========================
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-capacityreservationspecification.html
// Will require creating an L2 for AWS::EC2::CapacityReservation
// capacityReservationSpecification: undefined,
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-cpuoptions.html
// cpuOptions: undefined,
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-elasticgpuspecification.html
// elasticGpuSpecifications: undefined,
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-elasticinferenceaccelerators
// elasticInferenceAccelerators: undefined,
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-kernelid
// kernelId: undefined,
// ramDiskId: undefined,
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-licensespecifications
// Also not implemented in Instance L2
// licenseSpecifications: undefined,
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions
// metadataOptions: undefined,
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-tagspecifications
// Should be implemented via the Tagging aspect in CDK core. Complication will be that this tagging interface is very unique to LaunchTemplates.
// tagSpecification: undefined
// CDK has no abstraction for Network Interfaces yet.
// networkInterfaces: undefined,
// CDK has no abstraction for Placement yet.
// placement: undefined,
},
});
Tags.of(this).add(NAME_TAG, this.node.path);
this.defaultVersionNumber = resource.attrDefaultVersionNumber;
this.latestVersionNumber = resource.attrLatestVersionNumber;
this.launchTemplateId = resource.ref;
this.versionNumber = Token.asString(resource.getAtt('LatestVersionNumber'));
if (props.requireImdsv2) {
Aspects.of(this).add(new LaunchTemplateRequireImdsv2Aspect());
}
}