Skip to content

Commit 9744a82

Browse files
authored
feat(ec2): SSM sessions (#24673)
It's not too hard to enable SSM Session Manager to Instances and AutoScalingGroups (it's a matter of picking the right AMI and adding the right managed policy to the instance role). This PR adds a single boolean to turn on the policy directly and advertises the feature in the README for people who might otherwise not know this feature exists. Also consistentize the use and explanation of `MachineImage.latestAmazonLinux` a bit. ---- *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
1 parent cabff71 commit 9744a82

File tree

7 files changed

+198
-18
lines changed

7 files changed

+198
-18
lines changed

packages/@aws-cdk/aws-autoscaling/README.md

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,11 @@ declare const vpc: ec2.Vpc;
2525
new autoscaling.AutoScalingGroup(this, 'ASG', {
2626
vpc,
2727
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO),
28-
machineImage: new ec2.AmazonLinuxImage() // get the latest Amazon Linux image
28+
29+
// The latest Amazon Linux image of a particular generation
30+
machineImage: ec2.MachineImage.latestAmazonLinux({
31+
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
32+
}),
2933
});
3034
```
3135

@@ -41,7 +45,9 @@ const mySecurityGroup = new ec2.SecurityGroup(this, 'SecurityGroup', { vpc });
4145
new autoscaling.AutoScalingGroup(this, 'ASG', {
4246
vpc,
4347
instanceType: ec2.InstanceType.of(ec2.InstanceClass.BURSTABLE2, ec2.InstanceSize.MICRO),
44-
machineImage: new ec2.AmazonLinuxImage(),
48+
machineImage: ec2.MachineImage.latestAmazonLinux({
49+
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
50+
}),
4551
securityGroup: mySecurityGroup,
4652
});
4753
```
@@ -538,6 +544,40 @@ new autoscaling.AutoScalingGroup(this, 'ASG', {
538544
});
539545
```
540546

547+
## Connecting to your instances using SSM Session Manager
548+
549+
SSM Session Manager makes it possible to connect to your instances from the
550+
AWS Console, without preparing SSH keys.
551+
552+
To do so, you need to:
553+
554+
* Use an image with [SSM agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) installed
555+
and configured. [Many images come with SSM Agent
556+
preinstalled](https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html), otherwise you
557+
may need to manually put instructions to [install SSM
558+
Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-manual-agent-install.html) into your
559+
instance's UserData or use EC2 Init).
560+
* Create the AutoScalingGroup with `ssmSessionPermissions: true`.
561+
562+
If these conditions are met, you can connect to the instance from the EC2 Console. Example:
563+
564+
```ts
565+
declare const vpc: ec2.Vpc;
566+
567+
new autoscaling.AutoScalingGroup(this, 'ASG', {
568+
vpc,
569+
instanceType: ec2.InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.MICRO),
570+
571+
// Amazon Linux 2 comes with SSM Agent by default
572+
machineImage: ec2.MachineImage.latestAmazonLinux({
573+
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
574+
}),
575+
576+
// Turn on SSM
577+
ssmSessionPermissions: true,
578+
});
579+
```
580+
541581
## Configuring Instance Metadata Service (IMDS)
542582

543583
### Toggling IMDSv1
@@ -596,13 +636,13 @@ autoScalingGroup.addWarmPool({
596636

597637
### Default Instance Warming
598638

599-
You can use the default instance warmup feature to improve the Amazon CloudWatch metrics used for dynamic scaling.
600-
When default instance warmup is not enabled, each instance starts contributing usage data to the aggregated metrics
601-
as soon as the instance reaches the InService state. However, if you enable default instance warmup, this lets
639+
You can use the default instance warmup feature to improve the Amazon CloudWatch metrics used for dynamic scaling.
640+
When default instance warmup is not enabled, each instance starts contributing usage data to the aggregated metrics
641+
as soon as the instance reaches the InService state. However, if you enable default instance warmup, this lets
602642
your instances finish warming up before they contribute the usage data.
603643

604-
To optimize the performance of scaling policies that scale continuously, such as target tracking and step scaling
605-
policies, we strongly recommend that you enable the default instance warmup, even if its value is set to 0 seconds.
644+
To optimize the performance of scaling policies that scale continuously, such as target tracking and step scaling
645+
policies, we strongly recommend that you enable the default instance warmup, even if its value is set to 0 seconds.
606646

607647
To set up Default Instance Warming for an autoscaling group, simply pass it in as a prop
608648

packages/@aws-cdk/aws-autoscaling/lib/auto-scaling-group.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,23 @@ export interface CommonAutoScalingGroupProps {
365365
*
366366
*/
367367
readonly capacityRebalance?: boolean;
368+
369+
/**
370+
* Add SSM session permissions to the instance role
371+
*
372+
* Setting this to `true` adds the necessary permissions to connect
373+
* to the instance using SSM Session Manager. You can do this
374+
* from the AWS Console.
375+
*
376+
* NOTE: Setting this flag to `true` may not be enough by itself.
377+
* You must also use an AMI that comes with the SSM Agent, or install
378+
* the SSM Agent yourself. See
379+
* [Working with SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html)
380+
* in the SSM Developer Guide.
381+
*
382+
* @default false
383+
*/
384+
readonly ssmSessionPermissions?: boolean;
368385
}
369386

370387
/**
@@ -1278,6 +1295,10 @@ export class AutoScalingGroup extends AutoScalingGroupBase implements
12781295

12791296
this.grantPrincipal = this._role;
12801297

1298+
if (props.ssmSessionPermissions) {
1299+
this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'));
1300+
}
1301+
12811302
const iamProfile = new iam.CfnInstanceProfile(this, 'InstanceProfile', {
12821303
roles: [this.role.roleName],
12831304
});

packages/@aws-cdk/aws-autoscaling/test/auto-scaling-group.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,6 +2051,30 @@ test('add price-capacity-optimized', () => {
20512051
});
20522052
});
20532053

2054+
test('ssm permissions adds right managed policy', () => {
2055+
// GIVEN
2056+
const stack = new cdk.Stack();
2057+
2058+
// WHEN
2059+
new autoscaling.AutoScalingGroup(stack, 'mip-asg', {
2060+
vpc: mockVpc(stack),
2061+
machineImage: new AmazonLinuxImage(),
2062+
instanceType: InstanceType.of(ec2.InstanceClass.T3, ec2.InstanceSize.LARGE),
2063+
ssmSessionPermissions: true,
2064+
});
2065+
2066+
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', {
2067+
ManagedPolicyArns: [
2068+
{
2069+
'Fn::Join': ['', [
2070+
'arn:',
2071+
{ Ref: 'AWS::Partition' },
2072+
':iam::aws:policy/AmazonSSMManagedInstanceCore',
2073+
]],
2074+
},
2075+
],
2076+
});
2077+
});
20542078

20552079
function mockSecurityGroup(stack: cdk.Stack) {
20562080
return ec2.SecurityGroup.fromSecurityGroupId(stack, 'MySG', 'most-secure');

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

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -788,7 +788,7 @@ AMIs control the OS that gets launched when you start your EC2 instance. The EC2
788788
library contains constructs to select the AMI you want to use.
789789

790790
Depending on the type of AMI, you select it a different way. Here are some
791-
examples of things you might want to use:
791+
examples of images you might want to use:
792792

793793
[example of creating images](test/example.images.lit.ts)
794794

@@ -1039,27 +1039,27 @@ care of restarting your instance if it ever fails.
10391039
declare const vpc: ec2.Vpc;
10401040
declare const instanceType: ec2.InstanceType;
10411041

1042-
// AWS Linux
1042+
// Amazon Linux 1
10431043
new ec2.Instance(this, 'Instance1', {
10441044
vpc,
10451045
instanceType,
1046-
machineImage: new ec2.AmazonLinuxImage(),
1046+
machineImage: ec2.MachineImage.latestAmazonLinux(),
10471047
});
10481048

1049-
// AWS Linux 2
1049+
// Amazon Linux 2
10501050
new ec2.Instance(this, 'Instance2', {
10511051
vpc,
10521052
instanceType,
1053-
machineImage: new ec2.AmazonLinuxImage({
1053+
machineImage: ec2.MachineImage.latestAmazonLinux({
10541054
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
10551055
}),
10561056
});
10571057

1058-
// AWS Linux 2 with kernel 5.x
1058+
// Amazon Linux 2 with kernel 5.x
10591059
new ec2.Instance(this, 'Instance3', {
10601060
vpc,
10611061
instanceType,
1062-
machineImage: new ec2.AmazonLinuxImage({
1062+
machineImage: ec2.MachineImage.latestAmazonLinux({
10631063
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
10641064
kernel: ec2.AmazonLinuxKernel.KERNEL5_X,
10651065
}),
@@ -1069,7 +1069,7 @@ new ec2.Instance(this, 'Instance3', {
10691069
new ec2.Instance(this, 'Instance4', {
10701070
vpc,
10711071
instanceType,
1072-
machineImage: new ec2.AmazonLinuxImage({
1072+
machineImage: ec2.MachineImage.latestAmazonLinux({
10731073
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2022,
10741074
}),
10751075
});
@@ -1078,7 +1078,7 @@ new ec2.Instance(this, 'Instance4', {
10781078
new ec2.Instance(this, 'Instance5', {
10791079
vpc,
10801080
instanceType: ec2.InstanceType.of(ec2.InstanceClass.C7G, ec2.InstanceSize.LARGE),
1081-
machineImage: new ec2.AmazonLinuxImage({
1081+
machineImage: ec2.MachineImage.latestAmazonLinux({
10821082
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
10831083
cpuType: ec2.AmazonLinuxCpuType.ARM_64,
10841084
}),
@@ -1669,7 +1669,9 @@ The following demonstrates how to create a launch template with an Amazon Machin
16691669
declare const vpc: ec2.Vpc;
16701670

16711671
const template = new ec2.LaunchTemplate(this, 'LaunchTemplate', {
1672-
machineImage: ec2.MachineImage.latestAmazonLinux(),
1672+
machineImage: ec2.MachineImage.latestAmazonLinux({
1673+
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
1674+
}),
16731675
securityGroup: new ec2.SecurityGroup(this, 'LaunchTemplateSG', {
16741676
vpc: vpc,
16751677
}),
@@ -1699,7 +1701,42 @@ declare const instanceType: ec2.InstanceType;
16991701
new ec2.Instance(this, 'Instance1', {
17001702
vpc,
17011703
instanceType,
1702-
machineImage: new ec2.AmazonLinuxImage(),
1704+
machineImage: ec2.MachineImage.latestAmazonLinux(),
17031705
detailedMonitoring: true,
17041706
});
17051707
```
1708+
1709+
## Connecting to your instances using SSM Session Manager
1710+
1711+
SSM Session Manager makes it possible to connect to your instances from the
1712+
AWS Console, without preparing SSH keys.
1713+
1714+
To do so, you need to:
1715+
1716+
* Use an image with [SSM agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html) installed
1717+
and configured. [Many images come with SSM Agent
1718+
preinstalled](https://docs.aws.amazon.com/systems-manager/latest/userguide/ami-preinstalled-agent.html), otherwise you
1719+
may need to manually put instructions to [install SSM
1720+
Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-manual-agent-install.html) into your
1721+
instance's UserData or use EC2 Init).
1722+
* Create the instance with `ssmSessionPermissions: true`.
1723+
1724+
If these conditions are met, you can connect to the instance from the EC2 Console. Example:
1725+
1726+
```ts
1727+
declare const vpc: ec2.Vpc;
1728+
declare const instanceType: ec2.InstanceType;
1729+
1730+
new ec2.Instance(this, 'Instance1', {
1731+
vpc,
1732+
instanceType,
1733+
1734+
// Amazon Linux 2 comes with SSM Agent by default
1735+
machineImage: ec2.MachineImage.latestAmazonLinux({
1736+
generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
1737+
}),
1738+
1739+
// Turn on SSM
1740+
ssmSessionPermissions: true,
1741+
});
1742+
```

packages/@aws-cdk/aws-ec2/lib/instance.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,23 @@ export interface InstanceProps {
254254
* @default - false
255255
*/
256256
readonly detailedMonitoring?: boolean;
257+
258+
/**
259+
* Add SSM session permissions to the instance role
260+
*
261+
* Setting this to `true` adds the necessary permissions to connect
262+
* to the instance using SSM Session Manager. You can do this
263+
* from the AWS Console.
264+
*
265+
* NOTE: Setting this flag to `true` may not be enough by itself.
266+
* You must also use an AMI that comes with the SSM Agent, or install
267+
* the SSM Agent yourself. See
268+
* [Working with SSM Agent](https://docs.aws.amazon.com/systems-manager/latest/userguide/ssm-agent.html)
269+
* in the SSM Developer Guide.
270+
*
271+
* @default false
272+
*/
273+
readonly ssmSessionPermissions?: boolean;
257274
}
258275

259276
/**
@@ -342,6 +359,10 @@ export class Instance extends Resource implements IInstance {
342359
});
343360
this.grantPrincipal = this.role;
344361

362+
if (props.ssmSessionPermissions) {
363+
this.role.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName('AmazonSSMManagedInstanceCore'));
364+
}
365+
345366
const iamProfile = new iam.CfnInstanceProfile(this, 'InstanceProfile', {
346367
roles: [this.role.roleName],
347368
});

packages/@aws-cdk/aws-ec2/lib/machine-image.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,20 @@ export abstract class MachineImage {
3939
* deployment. Be aware this will cause your instances to be replaced when a
4040
* new version of the image becomes available. Do not store stateful information
4141
* on the instance if you are using this image.
42+
*
43+
* N.B.: "latest" in the name of this function indicates that it always uses the most recent
44+
* image of a particular generation of Amazon Linux, not that it uses the "latest generation".
45+
* For backwards compatibility, this function uses Amazon Linux 1 if no generation
46+
* is specified.
47+
*
48+
* Specify the desired generation using the `generation` property:
49+
*
50+
* ```ts
51+
* ec2.MachineImage.latestAmazonLinux({
52+
* // Use Amazon Linux 2
53+
* generation: ec2.AmazonLinuxGeneration.AMAZON_LINUX_2,
54+
* })
55+
* ```
4256
*/
4357
public static latestAmazonLinux(props?: AmazonLinuxImageProps): IMachineImage {
4458
return new AmazonLinuxImage(props);

packages/@aws-cdk/aws-ec2/test/instance.test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,3 +602,26 @@ test('cause replacement from s3 asset in userdata', () => {
602602
}),
603603
}));
604604
});
605+
606+
test('ssm permissions adds right managed policy', () => {
607+
// WHEN
608+
new Instance(stack, 'InstanceOne', {
609+
vpc,
610+
machineImage: new AmazonLinuxImage(),
611+
instanceType: InstanceType.of(InstanceClass.T3, InstanceSize.LARGE),
612+
ssmSessionPermissions: true,
613+
});
614+
615+
Template.fromStack(stack).hasResourceProperties('AWS::IAM::Role', {
616+
ManagedPolicyArns: [
617+
{
618+
'Fn::Join': ['', [
619+
'arn:',
620+
{ Ref: 'AWS::Partition' },
621+
':iam::aws:policy/AmazonSSMManagedInstanceCore',
622+
]],
623+
},
624+
],
625+
});
626+
});
627+

0 commit comments

Comments
 (0)