Skip to content

Commit

Permalink
feat(aws-ec2): add metadata options support for launchTemplate constr…
Browse files Browse the repository at this point in the history
…uct (#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*
  • Loading branch information
pahud authored Oct 10, 2022
1 parent 39089f5 commit 9297bd0
Show file tree
Hide file tree
Showing 14 changed files with 787 additions and 7 deletions.
12 changes: 12 additions & 0 deletions packages/@aws-cdk/aws-ec2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1509,6 +1509,18 @@ const template = new ec2.LaunchTemplate(this, 'LaunchTemplate', {
});
```

And the following demonstrates how to enable metadata options support.

```ts
new ec2.LaunchTemplate(this, 'LaunchTemplate', {
httpEndpoint: true,
httpProtocolIpv6: true,
httpPutResponseHopLimit: 1,
httpTokens: ec2.LaunchTemplateHttpTokens.REQUIRED,
instanceMetadataTags: true,
});
```

## Detailed Monitoring

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/).
Expand Down
101 changes: 94 additions & 7 deletions packages/@aws-cdk/aws-ec2/lib/launch-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ import {
TagType,
Tags,
Token,
Aspects,
} from '@aws-cdk/core';
import { Construct } from 'constructs';
import { LaunchTemplateRequireImdsv2Aspect } from '.';
import { Connections, IConnectable } from './connections';
import { CfnLaunchTemplate } from './ec2.generated';
import { InstanceType } from './instance-types';
Expand Down Expand Up @@ -193,6 +191,23 @@ export interface LaunchTemplateSpotOptions {
readonly validUntil?: Expiration;
};

/**
* The state of token usage for your instance metadata requests.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httptokens
*/
export enum LaunchTemplateHttpTokens {
/**
* If the state is optional, you can choose to retrieve instance metadata with or without a signed token header on your request.
*/
OPTIONAL = 'optional',
/**
* If the state is required, you must send a signed token header with any instance metadata retrieval requests. In this state,
* retrieving the IAM role credentials always returns the version 2.0 credentials; the version 1.0 credentials are not available.
*/
REQUIRED = 'required',
}

/**
* Properties of a LaunchTemplate.
*/
Expand Down Expand Up @@ -341,6 +356,52 @@ export interface LaunchTemplateProps {
* @default - false
*/
readonly requireImdsv2?: boolean;

/**
* Enables or disables the HTTP metadata endpoint on your instances.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httpendpoint
*
* @default true
*/
readonly httpEndpoint?: boolean;

/**
* Enables or disables the IPv6 endpoint for the instance metadata service.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httpprotocolipv6
*
* @default true
*/
readonly httpProtocolIpv6?: boolean;

/**
* The desired HTTP PUT response hop limit for instance metadata requests. The larger the number, the further instance metadata requests can travel.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httpputresponsehoplimit
*
* @default 1
*/
readonly httpPutResponseHopLimit?: number;

/**
* The state of token usage for your instance metadata requests. The default state is `optional` if not specified. However,
* if requireImdsv2 is true, the state must be `required`.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httptokens
*
* @default LaunchTemplateHttpTokens.OPTIONAL
*/
readonly httpTokens?: LaunchTemplateHttpTokens;

/**
* 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.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-instancemetadatatags
*
* @default false
*/
readonly instanceMetadataTags?: boolean;
}

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

// Basic validation of the provided httpPutResponseHopLimit
if (props.httpPutResponseHopLimit !== undefined && (props.httpPutResponseHopLimit < 1 || props.httpPutResponseHopLimit > 64)) {
// See: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-ec2-launchtemplate-launchtemplatedata-metadataoptions.html#cfn-ec2-launchtemplate-launchtemplatedata-metadataoptions-httpputresponsehoplimit
Annotations.of(this).addError('HttpPutResponseHopLimit must between 1 and 64');
}

this.role = props.role;
this._grantPrincipal = this.role;
const iamProfile: iam.CfnInstanceProfile | undefined = this.role ? new iam.CfnInstanceProfile(this, 'Profile', {
Expand Down Expand Up @@ -639,6 +706,7 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr
securityGroupIds: securityGroupsToken,
tagSpecifications: tagsToken,
userData: userDataToken,
metadataOptions: this.renderMetadataOptions(props),

// Fields not yet implemented:
// ==========================
Expand All @@ -663,9 +731,6 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr
// 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
Expand All @@ -686,9 +751,31 @@ export class LaunchTemplate extends Resource implements ILaunchTemplate, iam.IGr
this.latestVersionNumber = resource.attrLatestVersionNumber;
this.launchTemplateId = resource.ref;
this.versionNumber = Token.asString(resource.getAtt('LatestVersionNumber'));
}

if (props.requireImdsv2) {
Aspects.of(this).add(new LaunchTemplateRequireImdsv2Aspect());
private renderMetadataOptions(props: LaunchTemplateProps) {
let requireMetadataOptions = false;
// if requireImdsv2 is true, httpTokens must be required.
if (props.requireImdsv2 === true && props.httpTokens === LaunchTemplateHttpTokens.OPTIONAL) {
Annotations.of(this).addError('httpTokens must be required when requireImdsv2 is true');
}
if (props.httpEndpoint !== undefined || props.httpProtocolIpv6 !== undefined || props.httpPutResponseHopLimit !== undefined ||
props.httpTokens !== undefined || props.instanceMetadataTags !== undefined || props.requireImdsv2 === true) {
requireMetadataOptions = true;
}
if (requireMetadataOptions) {
return {
httpEndpoint: props.httpEndpoint === true ? 'enabled' :
props.httpEndpoint === false ? 'disabled' : undefined,
httpProtocolIpv6: props.httpProtocolIpv6 === true ? 'enabled' :
props.httpProtocolIpv6 === false ? 'disabled' : undefined,
httpPutResponseHopLimit: props.httpPutResponseHopLimit,
httpTokens: props.requireImdsv2 === true ? LaunchTemplateHttpTokens.REQUIRED : props.httpTokens,
instanceMetadataTags: props.instanceMetadataTags === true ? 'enabled' :
props.instanceMetadataTags === false ? 'disabled' : undefined,
};
} else {
return undefined;
}
}

Expand Down
24 changes: 24 additions & 0 deletions packages/@aws-cdk/aws-ec2/test/integ.launch-template.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import * as cdk from '@aws-cdk/core';
import * as integ from '@aws-cdk/integ-tests';
import * as ec2 from '../lib';


const app = new cdk.App();

const stack = new cdk.Stack(app, 'aws-cdk-ec2-lt-metadata-1');

new ec2.LaunchTemplate(stack, 'LT', {
httpEndpoint: true,
httpProtocolIpv6: true,
httpPutResponseHopLimit: 2,
httpTokens: ec2.LaunchTemplateHttpTokens.REQUIRED,
instanceMetadataTags: true,
});


new integ.IntegTest(app, 'LambdaTest', {
testCases: [stack],
});

app.synth();

Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "21.0.0",
"files": {
"21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22": {
"source": {
"path": "LambdaTestDefaultTestDeployAssert1AF2B360.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "21fbb51d7b23f6a6c262b46a9caee79d744a3ac019fd45422d988b96d44b2a22.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
},
"Rules": {
"CheckBootstrapVersion": {
"Assertions": [
{
"Assert": {
"Fn::Not": [
{
"Fn::Contains": [
[
"1",
"2",
"3",
"4",
"5"
],
{
"Ref": "BootstrapVersion"
}
]
}
]
},
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "21.0.0",
"files": {
"d17275411e4cfac0e49078863acdd9766783f143736b4534a3a0f1b5a4de118a": {
"source": {
"path": "TestStack.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "d17275411e4cfac0e49078863acdd9766783f143736b4534a3a0f1b5a4de118a.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
{
"Resources": {
"LTC4631592": {
"Type": "AWS::EC2::LaunchTemplate",
"Properties": {
"LaunchTemplateData": {
"MetadataOptions": {
"HttpEndpoint": "enabled",
"HttpProtocolIpv6": "enabled",
"HttpPutResponseHopLimit": 2,
"HttpTokens": "required",
"InstanceMetadataTags": "enabled"
},
"TagSpecifications": [
{
"ResourceType": "instance",
"Tags": [
{
"Key": "Name",
"Value": "TestStack/LT"
}
]
},
{
"ResourceType": "volume",
"Tags": [
{
"Key": "Name",
"Value": "TestStack/LT"
}
]
}
]
},
"TagSpecifications": [
{
"ResourceType": "launch-template",
"Tags": [
{
"Key": "Name",
"Value": "TestStack/LT"
}
]
}
]
}
}
},
"Parameters": {
"BootstrapVersion": {
"Type": "AWS::SSM::Parameter::Value<String>",
"Default": "/cdk-bootstrap/hnb659fds/version",
"Description": "Version of the CDK Bootstrap resources in this environment, automatically retrieved from SSM Parameter Store. [cdk:skip]"
}
},
"Rules": {
"CheckBootstrapVersion": {
"Assertions": [
{
"Assert": {
"Fn::Not": [
{
"Fn::Contains": [
[
"1",
"2",
"3",
"4",
"5"
],
{
"Ref": "BootstrapVersion"
}
]
}
]
},
"AssertDescription": "CDK bootstrap stack version 6 required. Please run 'cdk bootstrap' with a recent version of the CDK CLI."
}
]
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "21.0.0",
"files": {
"ea313fee581c8e898c158e9d123dd48345192689bb08a3f7e84716db61247c6c": {
"source": {
"path": "aws-cdk-ec2-lt-metadata-1.template.json",
"packaging": "file"
},
"destinations": {
"current_account-current_region": {
"bucketName": "cdk-hnb659fds-assets-${AWS::AccountId}-${AWS::Region}",
"objectKey": "ea313fee581c8e898c158e9d123dd48345192689bb08a3f7e84716db61247c6c.json",
"assumeRoleArn": "arn:${AWS::Partition}:iam::${AWS::AccountId}:role/cdk-hnb659fds-file-publishing-role-${AWS::AccountId}-${AWS::Region}"
}
}
}
},
"dockerImages": {}
}
Loading

0 comments on commit 9297bd0

Please sign in to comment.