packages/aws-cdk-lib/aws-ec2/lib/instance.ts (309 lines of code) (raw):
import { Construct } from 'constructs';
import { InstanceRequireImdsv2Aspect } from './aspects';
import { CloudFormationInit } from './cfn-init';
import { Connections, IConnectable } from './connections';
import { CfnInstance } from './ec2.generated';
import { InstanceType } from './instance-types';
import { IKeyPair } from './key-pair';
import { CpuCredits, InstanceInitiatedShutdownBehavior } from './launch-template';
import { IMachineImage, OperatingSystemType } from './machine-image';
import { IPlacementGroup } from './placement-group';
import { instanceBlockDeviceMappings } from './private/ebs-util';
import { ISecurityGroup, SecurityGroup } from './security-group';
import { UserData } from './user-data';
import { BlockDevice } from './volume';
import { IVpc, Subnet, SubnetSelection } from './vpc';
import * as iam from '../../aws-iam';
import { Annotations, Aspects, Duration, FeatureFlags, Fn, IResource, Lazy, Resource, Stack, Tags, Token, ValidationError } from '../../core';
import { md5hash } from '../../core/lib/helpers-internal';
import { addConstructMetadata, MethodMetadata } from '../../core/lib/metadata-resource';
import { mutatingAspectPrio32333 } from '../../core/lib/private/aspect-prio';
import * as cxapi from '../../cx-api';
/**
* Name tag constant
*/
const NAME_TAG: string = 'Name';
export interface IInstance extends IResource, IConnectable, iam.IGrantable {
/**
* The instance's ID
*
* @attribute
*/
readonly instanceId: string;
/**
* The availability zone the instance was launched in
*
* @attribute
*/
readonly instanceAvailabilityZone: string;
/**
* Private DNS name for this instance
* @attribute
*/
readonly instancePrivateDnsName: string;
/**
* Private IP for this instance
*
* @attribute
*/
readonly instancePrivateIp: string;
/**
* Publicly-routable DNS name for this instance.
*
* (May be an empty string if the instance does not have a public name).
*
* @attribute
*/
readonly instancePublicDnsName: string;
/**
* Publicly-routable IP address for this instance.
*
* (May be an empty string if the instance does not have a public IP).
*
* @attribute
*/
readonly instancePublicIp: string;
}
/**
* Properties of an EC2 Instance
*/
export interface InstanceProps {
/**
* Name of SSH keypair to grant access to instance
*
* @default - No SSH access will be possible.
* @deprecated - Use `keyPair` instead - https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.aws_ec2-readme.html#using-an-existing-ec2-key-pair
*/
readonly keyName?: string;
/**
* The SSH keypair to grant access to the instance.
*
* @default - No SSH access will be possible.
*/
readonly keyPair?: IKeyPair;
/**
* Where to place the instance within the VPC
*
* @default - Private subnets.
*/
readonly vpcSubnets?: SubnetSelection;
/**
* In which AZ to place the instance within the VPC
*
* @default - Random zone.
*/
readonly availabilityZone?: string;
/**
* Whether the instance could initiate connections to anywhere by default.
* This property is only used when you do not provide a security group.
*
* @default true
*/
readonly allowAllOutbound?: boolean;
/**
* Whether the instance could initiate IPv6 connections to anywhere by default.
* This property is only used when you do not provide a security group.
*
* @default false
*/
readonly allowAllIpv6Outbound?: boolean;
/**
* The length of time to wait for the resourceSignalCount
*
* The maximum value is 43200 (12 hours).
*
* @default Duration.minutes(5)
*/
readonly resourceSignalTimeout?: Duration;
/**
* VPC to launch the instance in.
*/
readonly vpc: IVpc;
/**
* Security Group to assign to this instance
*
* @default - create new security group
*/
readonly securityGroup?: ISecurityGroup;
/**
* Type of instance to launch
*/
readonly instanceType: InstanceType;
/**
* AMI to launch
*/
readonly machineImage: IMachineImage;
/**
* Specific UserData to use
*
* The UserData may still be mutated after creation.
*
* @default - A UserData object appropriate for the MachineImage's
* Operating System is created.
*/
readonly userData?: UserData;
/**
* Changes to the UserData force replacement
*
* Depending the EC2 instance type, changing UserData either
* restarts the instance or replaces the instance.
*
* - Instance store-backed instances are replaced.
* - EBS-backed instances are restarted.
*
* By default, restarting does not execute the new UserData so you
* will need a different mechanism to ensure the instance is restarted.
*
* Setting this to `true` will make the instance's Logical ID depend on the
* UserData, which will cause CloudFormation to replace it if the UserData
* changes.
*
* @default - true if `initOptions` is specified, false otherwise.
*/
readonly userDataCausesReplacement?: boolean;
/**
* An IAM role to associate with the instance profile assigned to this Auto Scaling Group.
*
* The role must be assumable by the service principal `ec2.amazonaws.com`:
* Note: You can provide an instanceProfile or a role, but not both.
*
* @example
* const role = new iam.Role(this, 'MyRole', {
* assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com')
* });
*
* @default - A role will automatically be created, it can be accessed via the `role` property
*/
readonly role?: iam.IRole;
/**
* The instance profile used to pass role information to EC2 instances.
*
* Note: You can provide an instanceProfile or a role, but not both.
*
* @default - No instance profile
*/
readonly instanceProfile?: iam.IInstanceProfile;
/**
* The name of the instance
*
* @default - CDK generated name
*/
readonly instanceName?: string;
/**
* Specifies whether to enable an instance launched in a VPC to perform NAT.
* This controls whether source/destination checking is enabled on the instance.
* A value of true means that checking is enabled, and false means that checking is disabled.
* The value must be false for the instance to perform NAT.
*
* @default true
*/
readonly sourceDestCheck?: boolean;
/**
* Specifies how block devices are exposed to the instance. You can specify virtual devices and EBS volumes.
*
* Each instance that is launched has an associated root device volume,
* either an Amazon EBS volume or an instance store volume.
* You can use block device mappings to specify additional EBS volumes or
* instance store volumes to attach to an instance when it is launched.
*
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/block-device-mapping-concepts.html
*
* @default - Uses the block device mapping of the AMI
*/
readonly blockDevices?: BlockDevice[];
/**
* Defines a private IP address to associate with an instance.
*
* Private IP should be available within the VPC that the instance is build within.
*
* @default - no association
*/
readonly privateIpAddress?: string;
/**
* Propagate the EC2 instance tags to the EBS volumes.
*
* @default - false
*/
readonly propagateTagsToVolumeOnCreation?: boolean;
/**
* Apply the given CloudFormation Init configuration to the instance at startup
*
* @default - no CloudFormation init
*/
readonly init?: CloudFormationInit;
/**
* Use the given options for applying CloudFormation Init
*
* Describes the configsets to use and the timeout to wait
*
* @default - default options
*/
readonly initOptions?: ApplyCloudFormationInitOptions;
/**
* Whether IMDSv2 should be required on this instance.
*
* @default - false
*/
readonly requireImdsv2?: boolean;
/**
* Whether "Detailed Monitoring" is enabled for this instance
* Keep in mind that Detailed Monitoring results in extra charges
*
* @see http://aws.amazon.com/cloudwatch/pricing/
* @default - false
*/
readonly detailedMonitoring?: boolean;
/**
* Add SSM session permissions to the instance role
*
* Setting this to `true` adds the necessary permissions to connect
* to the instance using SSM Session Manager. You can do this
* from the AWS Console.
*
* NOTE: Setting this flag to `true` may not be enough by itself.
* You must also use an AMI that comes with the SSM Agent, or install
* the SSM Agent yourself. See
* [Working with SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html)
* in the SSM Developer Guide.
*
* @default false
*/
readonly ssmSessionPermissions?: boolean;
/**
* Whether to associate a public IP address to the primary network interface attached to this instance.
*
* You cannot specify this property and `ipv6AddressCount` at the same time.
*
* @default - public IP address is automatically assigned based on default behavior
*/
readonly associatePublicIpAddress?: boolean;
/**
* Specifying the CPU credit type for burstable EC2 instance types (T2, T3, T3a, etc).
* The unlimited CPU credit option is not supported for T3 instances with a dedicated host.
*
* @default - T2 instances are standard, while T3, T4g, and T3a instances are unlimited.
*/
readonly creditSpecification?: CpuCredits;
/**
* Indicates whether the instance is optimized for Amazon EBS I/O.
*
* This optimization provides dedicated throughput to Amazon EBS and an optimized configuration stack to provide optimal Amazon EBS I/O performance.
* This optimization isn't available with all instance types.
* Additional usage charges apply when using an EBS-optimized instance.
*
* @default false
*/
readonly ebsOptimized?: boolean;
/**
* If true, the instance will not be able to be terminated using the Amazon EC2 console, CLI, or API.
*
* To change this attribute after launch, use [ModifyInstanceAttribute](https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyInstanceAttribute.html).
* Alternatively, if you set InstanceInitiatedShutdownBehavior to terminate, you can terminate the instance
* by running the shutdown command from the instance.
*
* @see http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-ec2-instance.html#cfn-ec2-instance-disableapitermination
*
* @default false
*/
readonly disableApiTermination?: boolean;
/**
* Indicates whether an instance stops or terminates when you initiate shutdown from the instance
* (using the operating system command for system shutdown).
*
* @see https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/terminating-instances.html#Using_ChangingInstanceInitiatedShutdownBehavior
*
* @default InstanceInitiatedShutdownBehavior.STOP
*/
readonly instanceInitiatedShutdownBehavior?: InstanceInitiatedShutdownBehavior;
/**
* The placement group that you want to launch the instance into.
*
* @default - no placement group will be used for this instance.
*/
readonly placementGroup?: IPlacementGroup;
/**
* Whether the instance is enabled for AWS Nitro Enclaves.
*
* Nitro Enclaves requires a Nitro-based virtualized parent instance with specific Intel/AMD with at least 4 vCPUs
* or Graviton with at least 2 vCPUs instance types and Linux/Windows host OS,
* while the enclave itself supports only Linux OS.
*
* You can't set both `enclaveEnabled` and `hibernationEnabled` to true on the same instance.
*
* @see https://docs.aws.amazon.com/enclaves/latest/user/nitro-enclave.html#nitro-enclave-reqs
*
* @default - false
*/
readonly enclaveEnabled?: boolean;
/**
* Whether the instance is enabled for hibernation.
*
* You can't set both `enclaveEnabled` and `hibernationEnabled` to true on the same instance.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-instance-hibernationoptions.html
*
* @default - false
*/
readonly hibernationEnabled?: boolean;
/**
* The number of IPv6 addresses to associate with the primary network interface.
*
* Amazon EC2 chooses the IPv6 addresses from the range of your subnet.
*
* You cannot specify this property and `associatePublicIpAddress` at the same time.
*
* @default - For instances associated with an IPv6 subnet, use 1; otherwise, use 0.
*/
readonly ipv6AddressCount?: number;
}
/**
* This represents a single EC2 instance
*/
export class Instance extends Resource implements IInstance {
/**
* The type of OS the instance is running.
*/
public readonly osType: OperatingSystemType;
/**
* Allows specify security group connections for the instance.
*/
public readonly connections: Connections;
/**
* The IAM role assumed by the instance.
*/
public readonly role: iam.IRole;
/**
* The principal to grant permissions to
*/
public readonly grantPrincipal: iam.IPrincipal;
/**
* UserData for the instance
*/
public readonly userData: UserData;
/**
* the underlying instance resource
*/
public readonly instance: CfnInstance;
/**
* @attribute
*/
public readonly instanceId: string;
/**
* @attribute
*/
public readonly instanceAvailabilityZone: string;
/**
* @attribute
*/
public readonly instancePrivateDnsName: string;
/**
* @attribute
*/
public readonly instancePrivateIp: string;
/**
* @attribute
*/
public readonly instancePublicDnsName: string;
/**
* @attribute
*/
public readonly instancePublicIp: string;
private readonly securityGroup: ISecurityGroup;
private readonly securityGroups: ISecurityGroup[] = [];
constructor(scope: Construct, id: string, props: InstanceProps) {
super(scope, id);
// Enhanced CDK Analytics Telemetry
addConstructMetadata(this, props);
if (props.initOptions && !props.init) {
throw new ValidationError('Setting \'initOptions\' requires that \'init\' is also set', this);
}
if (props.keyName && props.keyPair) {
throw new ValidationError('Cannot specify both of \'keyName\' and \'keyPair\'; prefer \'keyPair\'', this);
}
// if credit specification is set, then the instance type must be burstable
if (props.creditSpecification && !props.instanceType.isBurstable()) {
throw new ValidationError(`creditSpecification is supported only for T4g, T3a, T3, T2 instance type, got: ${props.instanceType.toString()}`, this);
}
if (props.securityGroup) {
this.securityGroup = props.securityGroup;
} else {
this.securityGroup = new SecurityGroup(this, 'InstanceSecurityGroup', {
vpc: props.vpc,
allowAllOutbound: props.allowAllOutbound !== false,
allowAllIpv6Outbound: props.allowAllIpv6Outbound,
});
}
this.connections = new Connections({ securityGroups: [this.securityGroup] });
this.securityGroups.push(this.securityGroup);
Tags.of(this).add(NAME_TAG, props.instanceName || this.node.path);
if (props.instanceProfile && props.role) {
throw new ValidationError('You cannot provide both instanceProfile and role', this);
}
let iamInstanceProfile: string | undefined = undefined;
if (props.instanceProfile?.role) {
this.role = props.instanceProfile.role;
iamInstanceProfile = props.instanceProfile.instanceProfileName;
} else {
this.role = props.role || new iam.Role(this, 'InstanceRole', {
assumedBy: new iam.ServicePrincipal('ec2.amazonaws.com'),
});
const iamProfile = new iam.CfnInstanceProfile(this, 'InstanceProfile', {
roles: [this.role.roleName],
});
iamInstanceProfile = iamProfile.ref;
}
this.grantPrincipal = this.role;
if (props.ssmSessionPermissions) {
this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'));
}
// 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 { subnets, hasPublic } = props.vpc.selectSubnets(props.vpcSubnets);
let subnet;
if (props.availabilityZone) {
const selected = subnets.filter(sn => sn.availabilityZone === props.availabilityZone);
if (selected.length === 1) {
subnet = selected[0];
} else {
Annotations.of(this).addError(`Need exactly 1 subnet to match AZ '${props.availabilityZone}', found ${selected.length}. Use a different availabilityZone.`);
}
} else {
if (subnets.length > 0) {
subnet = subnets[0];
} else {
Annotations.of(this).addError(`Did not find any subnets matching '${JSON.stringify(props.vpcSubnets)}', please use a different selection.`);
}
}
if (!subnet) {
// We got here and we don't have a subnet because of validation errors.
// Invent one on the spot so the code below doesn't fail.
subnet = Subnet.fromSubnetAttributes(this, 'DummySubnet', {
subnetId: 's-notfound',
availabilityZone: 'az-notfound',
});
}
// network interfaces array is set to configure the primary network interface if associatePublicIpAddress is true or false
const networkInterfaces = props.associatePublicIpAddress !== undefined
? [{
deviceIndex: '0',
associatePublicIpAddress: props.associatePublicIpAddress,
subnetId: subnet.subnetId,
groupSet: securityGroupsToken,
privateIpAddress: props.privateIpAddress,
}] : undefined;
if (props.keyPair && !props.keyPair._isOsCompatible(imageConfig.osType)) {
throw new ValidationError(`${props.keyPair.type} keys are not compatible with the chosen AMI`, this);
}
if (props.enclaveEnabled && props.hibernationEnabled) {
throw new ValidationError('You can\'t set both `enclaveEnabled` and `hibernationEnabled` to true on the same instance', this);
}
if (
props.ipv6AddressCount !== undefined &&
!Token.isUnresolved(props.ipv6AddressCount) &&
(props.ipv6AddressCount < 0 || !Number.isInteger(props.ipv6AddressCount))
) {
throw new ValidationError(`\'ipv6AddressCount\' must be a non-negative integer, got: ${props.ipv6AddressCount}`, this);
}
if (
props.ipv6AddressCount !== undefined &&
props.associatePublicIpAddress !== undefined) {
throw new ValidationError('You can\'t set both \'ipv6AddressCount\' and \'associatePublicIpAddress\'', this);
}
// if network interfaces array is configured then subnetId, securityGroupIds,
// and privateIpAddress are configured on the network interface level and
// there is no need to configure them on the instance level
this.instance = new CfnInstance(this, 'Resource', {
imageId: imageConfig.imageId,
keyName: props.keyPair?.keyPairName ?? props?.keyName,
instanceType: props.instanceType.toString(),
subnetId: networkInterfaces ? undefined : subnet.subnetId,
securityGroupIds: networkInterfaces ? undefined : securityGroupsToken,
networkInterfaces,
iamInstanceProfile,
userData: userDataToken,
availabilityZone: subnet.availabilityZone,
sourceDestCheck: props.sourceDestCheck,
blockDeviceMappings: props.blockDevices !== undefined ? instanceBlockDeviceMappings(this, props.blockDevices) : undefined,
privateIpAddress: networkInterfaces ? undefined : props.privateIpAddress,
propagateTagsToVolumeOnCreation: props.propagateTagsToVolumeOnCreation,
monitoring: props.detailedMonitoring,
creditSpecification: props.creditSpecification ? { cpuCredits: props.creditSpecification } : undefined,
ebsOptimized: props.ebsOptimized,
disableApiTermination: props.disableApiTermination,
instanceInitiatedShutdownBehavior: props.instanceInitiatedShutdownBehavior,
placementGroupName: props.placementGroup?.placementGroupName,
enclaveOptions: props.enclaveEnabled !== undefined ? { enabled: props.enclaveEnabled } : undefined,
hibernationOptions: props.hibernationEnabled !== undefined ? { configured: props.hibernationEnabled } : undefined,
ipv6AddressCount: props.ipv6AddressCount,
});
this.instance.node.addDependency(this.role);
// if associatePublicIpAddress is true, then there must be a dependency on internet connectivity
if (props.associatePublicIpAddress !== undefined && props.associatePublicIpAddress) {
const internetConnected = props.vpc.selectSubnets(props.vpcSubnets).internetConnectivityEstablished;
this.instance.node.addDependency(internetConnected);
}
if (!hasPublic && props.associatePublicIpAddress) {
throw new ValidationError("To set 'associatePublicIpAddress: true' you must select Public subnets (vpcSubnets: { subnetType: SubnetType.PUBLIC })", this);
}
this.osType = imageConfig.osType;
this.node.defaultChild = this.instance;
this.instanceId = this.instance.ref;
this.instanceAvailabilityZone = this.instance.attrAvailabilityZone;
this.instancePrivateDnsName = this.instance.attrPrivateDnsName;
this.instancePrivateIp = this.instance.attrPrivateIp;
this.instancePublicDnsName = this.instance.attrPublicDnsName;
this.instancePublicIp = this.instance.attrPublicIp;
// When feature flag is true, if both the resourceSignalTimeout and initOptions.timeout are set,
// the timeout is summed together. This logic is done in applyCloudFormationInit.
// This is because applyUpdatePolicies overwrites the timeout when both timeout fields are specified.
if (FeatureFlags.of(this).isEnabled(cxapi.EC2_SUM_TIMEOUT_ENABLED)) {
this.applyUpdatePolicies(props);
if (props.init) {
this.applyCloudFormationInit(props.init, props.initOptions);
}
} else {
if (props.init) {
this.applyCloudFormationInit(props.init, props.initOptions);
}
this.applyUpdatePolicies(props);
}
// Trigger replacement (via new logical ID) on user data change, if specified or cfn-init is being used.
//
// This is slightly tricky -- we need to resolve the UserData string (in order to get at actual Asset hashes,
// instead of the Token stringifications of them ('${Token[1234]}'). However, in the case of CFN Init usage,
// a UserData is going to contain the logicalID of the resource itself, which means infinite recursion if we
// try to naively resolve. We need a recursion breaker in this.
const originalLogicalId = Stack.of(this).getLogicalId(this.instance);
let recursing = false;
this.instance.overrideLogicalId(Lazy.uncachedString({
produce: (context) => {
if (recursing) { return originalLogicalId; }
if (!(props.userDataCausesReplacement ?? props.initOptions)) { return originalLogicalId; }
const fragments = new Array<string>();
recursing = true;
try {
fragments.push(JSON.stringify(context.resolve(this.userData.render())));
} finally {
recursing = false;
}
const digest = md5hash(fragments.join('')).slice(0, 16);
return `${originalLogicalId}${digest}`;
},
}));
if (props.requireImdsv2) {
Aspects.of(this).add(new InstanceRequireImdsv2Aspect(), {
priority: mutatingAspectPrio32333(this),
});
}
}
/**
* Add the security group to the instance.
*
* @param securityGroup: The security group to add
*/
@MethodMetadata()
public addSecurityGroup(securityGroup: ISecurityGroup): void {
this.securityGroups.push(securityGroup);
}
/**
* Add command to the startup script of the instance.
* The command must be in the scripting language supported by the instance's OS (i.e. Linux/Windows).
*/
@MethodMetadata()
public addUserData(...commands: string[]) {
this.userData.addCommands(...commands);
}
/**
* Adds a statement to the IAM role assumed by the instance.
*/
@MethodMetadata()
public addToRolePolicy(statement: iam.PolicyStatement) {
this.role.addToPrincipalPolicy(statement);
}
/**
* Use a CloudFormation Init configuration at instance startup
*
* This does the following:
*
* - Attaches the CloudFormation Init metadata to the Instance resource.
* - Add commands to the instance UserData to run `cfn-init` and `cfn-signal`.
* - Update the instance's CreationPolicy to wait for the `cfn-signal` commands.
*/
@MethodMetadata()
public applyCloudFormationInit(init: CloudFormationInit, options: ApplyCloudFormationInitOptions = {}) {
init.attach(this.instance, {
platform: this.osType,
instanceRole: this.role,
userData: this.userData,
configSets: options.configSets,
embedFingerprint: options.embedFingerprint,
printLog: options.printLog,
ignoreFailures: options.ignoreFailures,
includeRole: options.includeRole,
includeUrl: options.includeUrl,
});
this.waitForResourceSignal(options.timeout ?? Duration.minutes(5));
}
/**
* Wait for a single additional resource signal
*
* Add 1 to the current ResourceSignal Count and add the given timeout to the current timeout.
*
* Use this to pause the CloudFormation deployment to wait for the instances
* in the AutoScalingGroup to report successful startup during
* creation and updates. The UserData script needs to invoke `cfn-signal`
* with a success or failure code after it is done setting up the instance.
*/
private waitForResourceSignal(timeout: Duration) {
const oldResourceSignal = this.instance.cfnOptions.creationPolicy?.resourceSignal;
this.instance.cfnOptions.creationPolicy = {
...this.instance.cfnOptions.creationPolicy,
resourceSignal: {
count: (oldResourceSignal?.count ?? 0) + 1,
timeout: (oldResourceSignal?.timeout ? Duration.parse(oldResourceSignal?.timeout).plus(timeout) : timeout).toIsoString(),
},
};
}
/**
* Apply CloudFormation update policies for the instance
*/
private applyUpdatePolicies(props: InstanceProps) {
if (props.resourceSignalTimeout !== undefined) {
this.instance.cfnOptions.creationPolicy = {
...this.instance.cfnOptions.creationPolicy,
resourceSignal: {
...this.instance.cfnOptions.creationPolicy?.resourceSignal,
timeout: props.resourceSignalTimeout && props.resourceSignalTimeout.toIsoString(),
},
};
}
}
}
/**
* Options for applying CloudFormation init to an instance or instance group
*/
export interface ApplyCloudFormationInitOptions {
/**
* ConfigSet to activate
*
* @default ['default']
*/
readonly configSets?: string[];
/**
* Timeout waiting for the configuration to be applied
*
* @default Duration.minutes(5)
*/
readonly timeout?: Duration;
/**
* Force instance replacement by embedding a config fingerprint
*
* If `true` (the default), a hash of the config will be embedded into the
* UserData, so that if the config changes, the UserData changes.
*
* - If the EC2 instance is instance-store backed or
* `userDataCausesReplacement` is set, this will cause the instance to be
* replaced and the new configuration to be applied.
* - If the instance is EBS-backed and `userDataCausesReplacement` is not
* set, the change of UserData will make the instance restart but not be
* replaced, and the configuration will not be applied automatically.
*
* If `false`, no hash will be embedded, and if the CloudFormation Init
* config changes nothing will happen to the running instance. If a
* config update introduces errors, you will not notice until after the
* CloudFormation deployment successfully finishes and the next instance
* fails to launch.
*
* @default true
*/
readonly embedFingerprint?: boolean;
/**
* Print the results of running cfn-init to the Instance System Log
*
* By default, the output of running cfn-init is written to a log file
* on the instance. Set this to `true` to print it to the System Log
* (visible from the EC2 Console), `false` to not print it.
*
* (Be aware that the system log is refreshed at certain points in
* time of the instance life cycle, and successful execution may
* not always show up).
*
* @default true
*/
readonly printLog?: boolean;
/**
* Don't fail the instance creation when cfn-init fails
*
* You can use this to prevent CloudFormation from rolling back when
* instances fail to start up, to help in debugging.
*
* @default false
*/
readonly ignoreFailures?: boolean;
/**
* Include --url argument when running cfn-init and cfn-signal commands
*
* This will be the cloudformation endpoint in the deployed region
* e.g. https://cloudformation.us-east-1.amazonaws.com
*
* @default false
*/
readonly includeUrl?: boolean;
/**
* Include --role argument when running cfn-init and cfn-signal commands
*
* This will be the IAM instance profile attached to the EC2 instance
*
* @default false
*/
readonly includeRole?: boolean;
}