Skip to content

Commit 9297bd0

Browse files
authoredOct 10, 2022
feat(aws-ec2): add metadata options support for launchTemplate construct (#22312)
Support `metadataOptions` for `LaunchTemplate` in aws-ec2. Fixes: #21893 ---- ### All Submissions: * [x] Have you followed the guidelines in our [Contributing guide?](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md) ### Adding new Unconventional Dependencies: * [ ] This PR adds new unconventional dependencies following the process described [here](https://github.com/aws/aws-cdk/blob/main/CONTRIBUTING.md/#adding-new-unconventional-dependencies) ### New Features * [x] Have you added the new feature to an [integration test](https://github.com/aws/aws-cdk/blob/main/INTEGRATION_TESTS.md)? * [x] Did you use `yarn integ` to deploy the infrastructure and generate the snapshot (i.e. `yarn integ` without `--dry-run`)? *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent 39089f5 commit 9297bd0

14 files changed

+787
-7
lines changed
 

‎packages/@aws-cdk/aws-ec2/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -1509,6 +1509,18 @@ const template = new ec2.LaunchTemplate(this, 'LaunchTemplate', {
15091509
});
15101510
```
15111511

1512+
And the following demonstrates how to enable metadata options support.
1513+
1514+
```ts
1515+
new ec2.LaunchTemplate(this, 'LaunchTemplate', {
1516+
httpEndpoint: true,
1517+
httpProtocolIpv6: true,
1518+
httpPutResponseHopLimit: 1,
1519+
httpTokens: ec2.LaunchTemplateHttpTokens.REQUIRED,
1520+
instanceMetadataTags: true,
1521+
});
1522+
```
1523+
15121524
## Detailed Monitoring
15131525

15141526
The following demonstrates how to enable [Detailed Monitoring](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/using-cloudwatch-new.html) for an EC2 instance. Keep in mind that Detailed Monitoring results in [additional charges](http://aws.amazon.com/cloudwatch/pricing/).

‎packages/@aws-cdk/aws-ec2/lib/launch-template.ts

+94-7
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,8 @@ import {
1212
TagType,
1313
Tags,
1414
Token,
15-
Aspects,
1615
} from '@aws-cdk/core';
1716
import { Construct } from 'constructs';
18-
import { LaunchTemplateRequireImdsv2Aspect } from '.';
1917
import { Connections, IConnectable } from './connections';
2018
import { CfnLaunchTemplate } from './ec2.generated';
2119
import { InstanceType } from './instance-types';
@@ -193,6 +191,23 @@ export interface LaunchTemplateSpotOptions {
193191
readonly validUntil?: Expiration;
194192
};
195193

194+
/**
195+
* The state of token usage for your instance metadata requests.
196+
*
197+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httptokens
198+
*/
199+
export enum LaunchTemplateHttpTokens {
200+
/**
201+
* If the state is optional, you can choose to retrieve instance metadata with or without a signed token header on your request.
202+
*/
203+
OPTIONAL = 'optional',
204+
/**
205+
* If the state is required, you must send a signed token header with any instance metadata retrieval requests. In this state,
206+
* retrieving the IAM role credentials always returns the version 2.0 credentials; the version 1.0 credentials are not available.
207+
*/
208+
REQUIRED = 'required',
209+
}
210+
196211
/**
197212
* Properties of a LaunchTemplate.
198213
*/
@@ -341,6 +356,52 @@ export interface LaunchTemplateProps {
341356
* @default - false
342357
*/
343358
readonly requireImdsv2?: boolean;
359+
360+
/**
361+
* Enables or disables the HTTP metadata endpoint on your instances.
362+
*
363+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httpendpoint
364+
*
365+
* @default true
366+
*/
367+
readonly httpEndpoint?: boolean;
368+
369+
/**
370+
* Enables or disables the IPv6 endpoint for the instance metadata service.
371+
*
372+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httpprotocolipv6
373+
*
374+
* @default true
375+
*/
376+
readonly httpProtocolIpv6?: boolean;
377+
378+
/**
379+
* The desired HTTP PUT response hop limit for instance metadata requests. The larger the number, the further instance metadata requests can travel.
380+
*
381+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httpputresponsehoplimit
382+
*
383+
* @default 1
384+
*/
385+
readonly httpPutResponseHopLimit?: number;
386+
387+
/**
388+
* The state of token usage for your instance metadata requests. The default state is `optional` if not specified. However,
389+
* if requireImdsv2 is true, the state must be `required`.
390+
*
391+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httptokens
392+
*
393+
* @default LaunchTemplateHttpTokens.OPTIONAL
394+
*/
395+
readonly httpTokens?: LaunchTemplateHttpTokens;
396+
397+
/**
398+
* Set to enabled to allow access to instance tags from the instance metadata. Set to disabled to turn off access to instance tags from the instance metadata.
399+
*
400+
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-instancemetadatatags
401+
*
402+
* @default false
403+
*/
404+
readonly instanceMetadataTags?: boolean;
344405
}
345406

346407
/**
@@ -506,6 +567,12 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr
506567
Annotations.of(this).addError('Spot block duration must be exactly 1, 2, 3, 4, 5, or 6 hours.');
507568
}
508569

570+
// Basic validation of the provided httpPutResponseHopLimit
571+
if (props.httpPutResponseHopLimit !== undefined && (props.httpPutResponseHopLimit < 1 || props.httpPutResponseHopLimit > 64)) {
572+
// See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httpputresponsehoplimit
573+
Annotations.of(this).addError('HttpPutResponseHopLimit must between 1 and 64');
574+
}
575+
509576
this.role = props.role;
510577
this._grantPrincipal = this.role;
511578
const iamProfile: iam.CfnInstanceProfile | undefined = this.role ? new iam.CfnInstanceProfile(this, 'Profile', {
@@ -639,6 +706,7 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr
639706
securityGroupIds: securityGroupsToken,
640707
tagSpecifications: tagsToken,
641708
userData: userDataToken,
709+
metadataOptions: this.renderMetadataOptions(props),
642710

643711
// Fields not yet implemented:
644712
// ==========================
@@ -663,9 +731,6 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr
663731
// Also not implemented in Instance L2
664732
// licenseSpecifications: undefined,
665733

666-
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions
667-
// metadataOptions: undefined,
668-
669734
// https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata.html#cfn-ec2-launchtemplate-launchtemplatedata-tagspecifications
670735
// Should be implemented via the Tagging aspect in CDK core. Complication will be that this tagging interface is very unique to LaunchTemplates.
671736
// tagSpecification: undefined
@@ -686,9 +751,31 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr
686751
this.latestVersionNumber = resource.attrLatestVersionNumber;
687752
this.launchTemplateId = resource.ref;
688753
this.versionNumber = Token.asString(resource.getAtt('LatestVersionNumber'));
754+
}
689755

690-
if (props.requireImdsv2) {
691-
Aspects.of(this).add(new LaunchTemplateRequireImdsv2Aspect());
756+
private renderMetadataOptions(props: LaunchTemplateProps) {
757+
let requireMetadataOptions = false;
758+
// if requireImdsv2 is true, httpTokens must be required.
759+
if (props.requireImdsv2 === true && props.httpTokens === LaunchTemplateHttpTokens.OPTIONAL) {
760+
Annotations.of(this).addError('httpTokens must be required when requireImdsv2 is true');
761+
}
762+
if (props.httpEndpoint !== undefined || props.httpProtocolIpv6 !== undefined || props.httpPutResponseHopLimit !== undefined ||
763+
props.httpTokens !== undefined || props.instanceMetadataTags !== undefined || props.requireImdsv2 === true) {
764+
requireMetadataOptions = true;
765+
}
766+
if (requireMetadataOptions) {
767+
return {
768+
httpEndpoint: props.httpEndpoint === true ? 'enabled' :
769+
props.httpEndpoint === false ? 'disabled' : undefined,
770+
httpProtocolIpv6: props.httpProtocolIpv6 === true ? 'enabled' :
771+
props.httpProtocolIpv6 === false ? 'disabled' : undefined,
772+
httpPutResponseHopLimit: props.httpPutResponseHopLimit,
773+
httpTokens: props.requireImdsv2 === true ? LaunchTemplateHttpTokens.REQUIRED : props.httpTokens,
774+
instanceMetadataTags: props.instanceMetadataTags === true ? 'enabled' :
775+
props.instanceMetadataTags === false ? 'disabled' : undefined,
776+
};
777+
} else {
778+
return undefined;
692779
}
693780
}
694781

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import * as cdk from '@aws-cdk/core';
2+
import * as integ from '@aws-cdk/integ-tests';
3+
import * as ec2 from '../lib';
4+
5+
6+
const app = new cdk.App();
7+
8+
const stack = new cdk.Stack(app, 'aws-cdk-ec2-lt-metadata-1');
9+
10+
new ec2.LaunchTemplate(stack, 'LT', {
11+
httpEndpoint: true,
12+
httpProtocolIpv6: true,
13+
httpPutResponseHopLimit: 2,
14+
httpTokens: ec2.LaunchTemplateHttpTokens.REQUIRED,
15+
instanceMetadataTags: true,
16+
});
17+
18+
19+
new integ.IntegTest(app, 'LambdaTest', {
20+
testCases: [stack],
21+
});
22+
23+
app.synth();
24+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "21.0.0",
3+
"files": {
4+
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
5+
"source": {
6+
"path": "LambdaTestDefaultTestDeployAssert1AF2B360.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
{
2+
"Parameters": {
3+
"BootstrapVersion": {
4+
"Type": "AWS::SSM::Parameter::Value<String>",
5+
"Default": "/cdk-bootstrap/hnb659fds/version",
6+
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
7+
}
8+
},
9+
"Rules": {
10+
"CheckBootstrapVersion": {
11+
"Assertions": [
12+
{
13+
"Assert": {
14+
"Fn::Not": [
15+
{
16+
"Fn::Contains": [
17+
[
18+
"1",
19+
"2",
20+
"3",
21+
"4",
22+
"5"
23+
],
24+
{
25+
"Ref": "BootstrapVersion"
26+
}
27+
]
28+
}
29+
]
30+
},
31+
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
32+
}
33+
]
34+
}
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "21.0.0",
3+
"files": {
4+
"d17275411e4cfac0e49078863acdd9766783f143736b4534a3a0f1b5a4de118a": {
5+
"source": {
6+
"path": "TestStack.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "d17275411e4cfac0e49078863acdd9766783f143736b4534a3a0f1b5a4de118a.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{
2+
"Resources": {
3+
"LTC4631592": {
4+
"Type": "AWS::EC2::LaunchTemplate",
5+
"Properties": {
6+
"LaunchTemplateData": {
7+
"MetadataOptions": {
8+
"HttpEndpoint": "enabled",
9+
"HttpProtocolIpv6": "enabled",
10+
"HttpPutResponseHopLimit": 2,
11+
"HttpTokens": "required",
12+
"InstanceMetadataTags": "enabled"
13+
},
14+
"TagSpecifications": [
15+
{
16+
"ResourceType": "instance",
17+
"Tags": [
18+
{
19+
"Key": "Name",
20+
"Value": "TestStack/LT"
21+
}
22+
]
23+
},
24+
{
25+
"ResourceType": "volume",
26+
"Tags": [
27+
{
28+
"Key": "Name",
29+
"Value": "TestStack/LT"
30+
}
31+
]
32+
}
33+
]
34+
},
35+
"TagSpecifications": [
36+
{
37+
"ResourceType": "launch-template",
38+
"Tags": [
39+
{
40+
"Key": "Name",
41+
"Value": "TestStack/LT"
42+
}
43+
]
44+
}
45+
]
46+
}
47+
}
48+
},
49+
"Parameters": {
50+
"BootstrapVersion": {
51+
"Type": "AWS::SSM::Parameter::Value<String>",
52+
"Default": "/cdk-bootstrap/hnb659fds/version",
53+
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
54+
}
55+
},
56+
"Rules": {
57+
"CheckBootstrapVersion": {
58+
"Assertions": [
59+
{
60+
"Assert": {
61+
"Fn::Not": [
62+
{
63+
"Fn::Contains": [
64+
[
65+
"1",
66+
"2",
67+
"3",
68+
"4",
69+
"5"
70+
],
71+
{
72+
"Ref": "BootstrapVersion"
73+
}
74+
]
75+
}
76+
]
77+
},
78+
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
79+
}
80+
]
81+
}
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"version": "21.0.0",
3+
"files": {
4+
"ea313fee581c8e898c158e9d123dd48345192689bb08a3f7e84716db61247c6c": {
5+
"source": {
6+
"path": "aws-cdk-ec2-lt-metadata-1.template.json",
7+
"packaging": "file"
8+
},
9+
"destinations": {
10+
"current_account-current_region": {
11+
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
12+
"objectKey": "ea313fee581c8e898c158e9d123dd48345192689bb08a3f7e84716db61247c6c.json",
13+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
14+
}
15+
}
16+
}
17+
},
18+
"dockerImages": {}
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
{
2+
"Resources": {
3+
"LTC4631592": {
4+
"Type": "AWS::EC2::LaunchTemplate",
5+
"Properties": {
6+
"LaunchTemplateData": {
7+
"MetadataOptions": {
8+
"HttpEndpoint": "enabled",
9+
"HttpProtocolIpv6": "enabled",
10+
"HttpPutResponseHopLimit": 2,
11+
"HttpTokens": "required",
12+
"InstanceMetadataTags": "enabled"
13+
},
14+
"TagSpecifications": [
15+
{
16+
"ResourceType": "instance",
17+
"Tags": [
18+
{
19+
"Key": "Name",
20+
"Value": "aws-cdk-ec2-lt-metadata-1/LT"
21+
}
22+
]
23+
},
24+
{
25+
"ResourceType": "volume",
26+
"Tags": [
27+
{
28+
"Key": "Name",
29+
"Value": "aws-cdk-ec2-lt-metadata-1/LT"
30+
}
31+
]
32+
}
33+
]
34+
},
35+
"TagSpecifications": [
36+
{
37+
"ResourceType": "launch-template",
38+
"Tags": [
39+
{
40+
"Key": "Name",
41+
"Value": "aws-cdk-ec2-lt-metadata-1/LT"
42+
}
43+
]
44+
}
45+
]
46+
}
47+
}
48+
},
49+
"Parameters": {
50+
"BootstrapVersion": {
51+
"Type": "AWS::SSM::Parameter::Value<String>",
52+
"Default": "/cdk-bootstrap/hnb659fds/version",
53+
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
54+
}
55+
},
56+
"Rules": {
57+
"CheckBootstrapVersion": {
58+
"Assertions": [
59+
{
60+
"Assert": {
61+
"Fn::Not": [
62+
{
63+
"Fn::Contains": [
64+
[
65+
"1",
66+
"2",
67+
"3",
68+
"4",
69+
"5"
70+
],
71+
{
72+
"Ref": "BootstrapVersion"
73+
}
74+
]
75+
}
76+
]
77+
},
78+
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
79+
}
80+
]
81+
}
82+
}
83+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"version":"21.0.0"}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"version": "21.0.0",
3+
"testCases": {
4+
"LambdaTest/DefaultTest": {
5+
"stacks": [
6+
"aws-cdk-ec2-lt-metadata-1"
7+
],
8+
"assertionStack": "LambdaTest/DefaultTest/DeployAssert",
9+
"assertionStackName": "LambdaTestDefaultTestDeployAssert1AF2B360"
10+
}
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
{
2+
"version": "21.0.0",
3+
"artifacts": {
4+
"Tree": {
5+
"type": "cdk:tree",
6+
"properties": {
7+
"file": "tree.json"
8+
}
9+
},
10+
"aws-cdk-ec2-lt-metadata-1.assets": {
11+
"type": "cdk:asset-manifest",
12+
"properties": {
13+
"file": "aws-cdk-ec2-lt-metadata-1.assets.json",
14+
"requiresBootstrapStackVersion": 6,
15+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
16+
}
17+
},
18+
"aws-cdk-ec2-lt-metadata-1": {
19+
"type": "aws:cloudformation:stack",
20+
"environment": "aws://unknown-account/unknown-region",
21+
"properties": {
22+
"templateFile": "aws-cdk-ec2-lt-metadata-1.template.json",
23+
"validateOnSynth": false,
24+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}",
25+
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}",
26+
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/ea313fee581c8e898c158e9d123dd48345192689bb08a3f7e84716db61247c6c.json",
27+
"requiresBootstrapStackVersion": 6,
28+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
29+
"additionalDependencies": [
30+
"aws-cdk-ec2-lt-metadata-1.assets"
31+
],
32+
"lookupRole": {
33+
"arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}",
34+
"requiresBootstrapStackVersion": 8,
35+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
36+
}
37+
},
38+
"dependencies": [
39+
"aws-cdk-ec2-lt-metadata-1.assets"
40+
],
41+
"metadata": {
42+
"/aws-cdk-ec2-lt-metadata-1/LT/Resource": [
43+
{
44+
"type": "aws:cdk:logicalId",
45+
"data": "LTC4631592"
46+
}
47+
],
48+
"/aws-cdk-ec2-lt-metadata-1/BootstrapVersion": [
49+
{
50+
"type": "aws:cdk:logicalId",
51+
"data": "BootstrapVersion"
52+
}
53+
],
54+
"/aws-cdk-ec2-lt-metadata-1/CheckBootstrapVersion": [
55+
{
56+
"type": "aws:cdk:logicalId",
57+
"data": "CheckBootstrapVersion"
58+
}
59+
]
60+
},
61+
"displayName": "aws-cdk-ec2-lt-metadata-1"
62+
},
63+
"LambdaTestDefaultTestDeployAssert1AF2B360.assets": {
64+
"type": "cdk:asset-manifest",
65+
"properties": {
66+
"file": "LambdaTestDefaultTestDeployAssert1AF2B360.assets.json",
67+
"requiresBootstrapStackVersion": 6,
68+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
69+
}
70+
},
71+
"LambdaTestDefaultTestDeployAssert1AF2B360": {
72+
"type": "aws:cloudformation:stack",
73+
"environment": "aws://unknown-account/unknown-region",
74+
"properties": {
75+
"templateFile": "LambdaTestDefaultTestDeployAssert1AF2B360.template.json",
76+
"validateOnSynth": false,
77+
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-deploy-role-${AWS::AccountId}-${AWS::Region}",
78+
"cloudFormationExecutionRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-cfn-exec-role-${AWS::AccountId}-${AWS::Region}",
79+
"stackTemplateAssetObjectUrl": "s3://cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}/21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
80+
"requiresBootstrapStackVersion": 6,
81+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version",
82+
"additionalDependencies": [
83+
"LambdaTestDefaultTestDeployAssert1AF2B360.assets"
84+
],
85+
"lookupRole": {
86+
"arn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-lookup-role-${AWS::AccountId}-${AWS::Region}",
87+
"requiresBootstrapStackVersion": 8,
88+
"bootstrapStackVersionSsmParameter": "/cdk-bootstrap/hnb659fds/version"
89+
}
90+
},
91+
"dependencies": [
92+
"LambdaTestDefaultTestDeployAssert1AF2B360.assets"
93+
],
94+
"metadata": {
95+
"/LambdaTest/DefaultTest/DeployAssert/BootstrapVersion": [
96+
{
97+
"type": "aws:cdk:logicalId",
98+
"data": "BootstrapVersion"
99+
}
100+
],
101+
"/LambdaTest/DefaultTest/DeployAssert/CheckBootstrapVersion": [
102+
{
103+
"type": "aws:cdk:logicalId",
104+
"data": "CheckBootstrapVersion"
105+
}
106+
]
107+
},
108+
"displayName": "LambdaTest/DefaultTest/DeployAssert"
109+
}
110+
}
111+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
{
2+
"version": "tree-0.1",
3+
"tree": {
4+
"id": "App",
5+
"path": "",
6+
"children": {
7+
"Tree": {
8+
"id": "Tree",
9+
"path": "Tree",
10+
"constructInfo": {
11+
"fqn": "constructs.Construct",
12+
"version": "10.1.108"
13+
}
14+
},
15+
"aws-cdk-ec2-lt-metadata-1": {
16+
"id": "aws-cdk-ec2-lt-metadata-1",
17+
"path": "aws-cdk-ec2-lt-metadata-1",
18+
"children": {
19+
"LT": {
20+
"id": "LT",
21+
"path": "aws-cdk-ec2-lt-metadata-1/LT",
22+
"children": {
23+
"Resource": {
24+
"id": "Resource",
25+
"path": "aws-cdk-ec2-lt-metadata-1/LT/Resource",
26+
"attributes": {
27+
"aws:cdk:cloudformation:type": "AWS::EC2::LaunchTemplate",
28+
"aws:cdk:cloudformation:props": {
29+
"launchTemplateData": {
30+
"tagSpecifications": [
31+
{
32+
"resourceType": "instance",
33+
"tags": [
34+
{
35+
"key": "Name",
36+
"value": "aws-cdk-ec2-lt-metadata-1/LT"
37+
}
38+
]
39+
},
40+
{
41+
"resourceType": "volume",
42+
"tags": [
43+
{
44+
"key": "Name",
45+
"value": "aws-cdk-ec2-lt-metadata-1/LT"
46+
}
47+
]
48+
}
49+
],
50+
"metadataOptions": {
51+
"httpEndpoint": "enabled",
52+
"httpProtocolIpv6": "enabled",
53+
"httpPutResponseHopLimit": 2,
54+
"httpTokens": "required",
55+
"instanceMetadataTags": "enabled"
56+
}
57+
},
58+
"tagSpecifications": [
59+
{
60+
"resourceType": "launch-template",
61+
"tags": [
62+
{
63+
"key": "Name",
64+
"value": "aws-cdk-ec2-lt-metadata-1/LT"
65+
}
66+
]
67+
}
68+
]
69+
}
70+
},
71+
"constructInfo": {
72+
"fqn": "@aws-cdk/core.CfnResource",
73+
"version": "0.0.0"
74+
}
75+
}
76+
},
77+
"constructInfo": {
78+
"fqn": "@aws-cdk/core.Resource",
79+
"version": "0.0.0"
80+
}
81+
}
82+
},
83+
"constructInfo": {
84+
"fqn": "@aws-cdk/core.Stack",
85+
"version": "0.0.0"
86+
}
87+
},
88+
"LambdaTest": {
89+
"id": "LambdaTest",
90+
"path": "LambdaTest",
91+
"children": {
92+
"DefaultTest": {
93+
"id": "DefaultTest",
94+
"path": "LambdaTest/DefaultTest",
95+
"children": {
96+
"Default": {
97+
"id": "Default",
98+
"path": "LambdaTest/DefaultTest/Default",
99+
"constructInfo": {
100+
"fqn": "constructs.Construct",
101+
"version": "10.1.108"
102+
}
103+
},
104+
"DeployAssert": {
105+
"id": "DeployAssert",
106+
"path": "LambdaTest/DefaultTest/DeployAssert",
107+
"constructInfo": {
108+
"fqn": "@aws-cdk/core.Stack",
109+
"version": "0.0.0"
110+
}
111+
}
112+
},
113+
"constructInfo": {
114+
"fqn": "@aws-cdk/integ-tests.IntegTestCase",
115+
"version": "0.0.0"
116+
}
117+
}
118+
},
119+
"constructInfo": {
120+
"fqn": "@aws-cdk/integ-tests.IntegTest",
121+
"version": "0.0.0"
122+
}
123+
}
124+
},
125+
"constructInfo": {
126+
"fqn": "@aws-cdk/core.App",
127+
"version": "0.0.0"
128+
}
129+
}
130+
}

‎packages/@aws-cdk/aws-ec2/test/launch-template.test.ts

+144
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
InstanceInitiatedShutdownBehavior,
2222
InstanceType,
2323
LaunchTemplate,
24+
LaunchTemplateHttpTokens,
2425
OperatingSystemType,
2526
SecurityGroup,
2627
SpotInstanceInterruption,
@@ -760,3 +761,146 @@ describe('LaunchTemplate marketOptions', () => {
760761
});
761762
});
762763
});
764+
765+
describe('LaunchTemplate metadataOptions', () => {
766+
let app: App;
767+
let stack: Stack;
768+
769+
beforeEach(() => {
770+
app = new App();
771+
stack = new Stack(app);
772+
});
773+
774+
test.each([
775+
[true, 'enabled'],
776+
[false, 'disabled'],
777+
])('given httpEndpoint %p', (given: boolean, expected: string) => {
778+
// WHEN
779+
new LaunchTemplate(stack, 'Template', {
780+
httpEndpoint: given,
781+
});
782+
// THEN
783+
Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', {
784+
LaunchTemplateData: {
785+
MetadataOptions: {
786+
HttpEndpoint: expected,
787+
},
788+
},
789+
});
790+
});
791+
792+
test.each([
793+
[true, 'enabled'],
794+
[false, 'disabled'],
795+
])('given httpProtocolIpv6 %p', (given: boolean, expected: string) => {
796+
// WHEN
797+
new LaunchTemplate(stack, 'Template', {
798+
httpProtocolIpv6: given,
799+
});
800+
// THEN
801+
Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', {
802+
LaunchTemplateData: {
803+
MetadataOptions: {
804+
HttpProtocolIpv6: expected,
805+
},
806+
},
807+
});
808+
});
809+
810+
test.each([
811+
[1, 1],
812+
[2, 2],
813+
])('given httpPutResponseHopLimit %p', (given: number, expected: number) => {
814+
// WHEN
815+
new LaunchTemplate(stack, 'Template', {
816+
httpPutResponseHopLimit: given,
817+
});
818+
// THEN
819+
Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', {
820+
LaunchTemplateData: {
821+
MetadataOptions: {
822+
HttpPutResponseHopLimit: expected,
823+
},
824+
},
825+
});
826+
});
827+
828+
test.each([
829+
[LaunchTemplateHttpTokens.OPTIONAL, 'optional'],
830+
[LaunchTemplateHttpTokens.REQUIRED, 'required'],
831+
])('given httpTokens %p', (given: LaunchTemplateHttpTokens, expected: string) => {
832+
// WHEN
833+
new LaunchTemplate(stack, 'Template', {
834+
httpTokens: given,
835+
});
836+
// THEN
837+
Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', {
838+
LaunchTemplateData: {
839+
MetadataOptions: {
840+
HttpTokens: expected,
841+
},
842+
},
843+
});
844+
});
845+
846+
test.each([
847+
[true, 'enabled'],
848+
[false, 'disabled'],
849+
])('given instanceMetadataTags %p', (given: boolean, expected: string) => {
850+
// WHEN
851+
new LaunchTemplate(stack, 'Template', {
852+
instanceMetadataTags: given,
853+
});
854+
// THEN
855+
Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', {
856+
LaunchTemplateData: {
857+
MetadataOptions: {
858+
InstanceMetadataTags: expected,
859+
},
860+
},
861+
});
862+
});
863+
864+
test.each([
865+
[0, 1],
866+
[-1, 1],
867+
[1, 0],
868+
[64, 0],
869+
[65, 1],
870+
])('given instanceMetadataTags %p', (given: number, expected: number) => {
871+
// WHEN
872+
new LaunchTemplate(stack, 'Template', {
873+
httpPutResponseHopLimit: given,
874+
});
875+
// THEN
876+
const errors = Annotations.fromStack(stack).findError('/Default/Template', Match.anyValue());
877+
expect(errors).toHaveLength(expected);
878+
});
879+
880+
test('throw when requireImdsv2 is true and httpTokens is OPTIONAL', () => {
881+
// WHEN
882+
new LaunchTemplate(stack, 'Template', {
883+
requireImdsv2: true,
884+
httpTokens: LaunchTemplateHttpTokens.OPTIONAL,
885+
});
886+
// THEN
887+
const errors = Annotations.fromStack(stack).findError('/Default/Template', Match.anyValue());
888+
expect(errors[0].entry.data).toMatch(/httpTokens must be required when requireImdsv2 is true/);
889+
});
890+
test('httpTokens REQUIRED is allowed when requireImdsv2 is true', () => {
891+
// WHEN
892+
new LaunchTemplate(stack, 'Template', {
893+
requireImdsv2: true,
894+
httpTokens: LaunchTemplateHttpTokens.REQUIRED,
895+
});
896+
// THEN
897+
Template.fromStack(stack).hasResourceProperties('AWS::EC2::LaunchTemplate', {
898+
LaunchTemplateData: {
899+
MetadataOptions: {
900+
HttpTokens: 'required',
901+
},
902+
},
903+
});
904+
905+
});
906+
});

0 commit comments

Comments
 (0)
Please sign in to comment.