Create AWS Session Manager Accessible EC2

With CloudFormation

Alan Zhao
2 min readJun 29, 2023

Recently, I need to spin up an EC2 to conduct a series of testing within a private network. This is a one-time usage, hence I created a CloudFormation script that will create the EC2 and allow Session Manager to access it via SSH.

In order for Session Manager to access the EC2 in a private subnet, the following VPC endpoints have to be created:

  • com.amazonaws.[YOUR-REGION].ec2messages
  • com.amazonaws.[YOUR-REGION].ssmmessages
  • com.amazonaws.[YOUR-REGION].kms
  • com.amazonaws.[YOUR-REGION].ssm

The full CloudFormation script is below. Make sure you tear down the stack when done. The EC2 and VPC endpoints will incur cost.

AWSTemplateFormatVersion: "2010-09-09"
Description: >-
This template will create a VPC, private subnet, EC2 and other required resources
to support Session Manager to connect to the EC2 in private network.
It requires the setup of required VPC endpoints and will incurr cost.
It will take SSM a few minutes to register the instance after template is complete.

Parameters:
ImageId:
Type: String
# Amazon Linux 2023 x86_64 AMI
Default: ami-022e1a32d3f742bd8
Description: The EC2 AMI image ID

VpcCidrBlock:
Type: String
Default: 10.0.0.0/16
Description: CIDR block for the VPC

CreateVpcEndpoints:
Type: String
Default: true
Description: Whether or not to create VPC endpoints for SSM access to the EC2 in private subnet

Conditions:
ShouldCreateVpcEndpoints: !Equals [!Ref CreateVpcEndpoints, true]

Resources:
InstanceRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub ${AWS::StackName}-iam-role
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service: ec2.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

InstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub ${AWS::StackName}-instance-profile
Roles:
- !Ref InstanceRole

VPC:
Type: AWS::EC2::VPC
Properties:
CidrBlock: !Ref VpcCidrBlock
EnableDnsSupport: true
EnableDnsHostnames: true
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} VPC

Subnet:
Type: AWS::EC2::Subnet
Properties:
VpcId: !Ref VPC
CidrBlock: !Select [0, !Cidr [!Ref VpcCidrBlock, 1, 8]]
AvailabilityZone: !Select [0, !GetAZs ""]
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} subnet

SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: !Sub Security Group for ${AWS::StackName} EC2 instances in VPC
VpcId: !Ref VPC

Instance:
Type: AWS::EC2::Instance
Properties:
ImageId: !Ref ImageId
InstanceType: t2.micro
IamInstanceProfile: !Ref InstanceProfile
SubnetId: !Ref Subnet
SecurityGroupIds:
- !Ref SecurityGroup
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} EC2

#
# VPC endpoints
#

SecurityGroupVpcEndpoint:
Type: AWS::EC2::SecurityGroup
Condition: ShouldCreateVpcEndpoints
Properties:
GroupDescription: !Sub Security Group for ${AWS::StackName} VPC endpoints
VpcId: !Ref VPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: !GetAtt VPC.CidrBlock

EC2MessagesEndpoint:
Type: AWS::EC2::VPCEndpoint
Condition: ShouldCreateVpcEndpoints
Properties:
VpcEndpointType: Interface
ServiceName: !Sub com.amazonaws.${AWS::Region}.ec2messages
VpcId: !Ref VPC
PrivateDnsEnabled: true
SubnetIds:
- !Ref Subnet
SecurityGroupIds:
- !Ref SecurityGroupVpcEndpoint

SSMMessagesEndpoint:
Type: AWS::EC2::VPCEndpoint
Condition: ShouldCreateVpcEndpoints
Properties:
VpcEndpointType: Interface
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssmmessages
VpcId: !Ref VPC
PrivateDnsEnabled: true
SubnetIds:
- !Ref Subnet
SecurityGroupIds:
- !Ref SecurityGroupVpcEndpoint

KMSEndpoint:
Type: AWS::EC2::VPCEndpoint
Condition: ShouldCreateVpcEndpoints
Properties:
VpcEndpointType: Interface
ServiceName: !Sub com.amazonaws.${AWS::Region}.kms
VpcId: !Ref VPC
PrivateDnsEnabled: true
SubnetIds:
- !Ref Subnet
SecurityGroupIds:
- !Ref SecurityGroupVpcEndpoint

SSMEndpoint:
Type: AWS::EC2::VPCEndpoint
Condition: ShouldCreateVpcEndpoints
Properties:
VpcEndpointType: Interface
ServiceName: !Sub com.amazonaws.${AWS::Region}.ssm
VpcId: !Ref VPC
PrivateDnsEnabled: true
SubnetIds:
- !Ref Subnet
SecurityGroupIds:
- !Ref SecurityGroupVpcEndpoint

Happy coding!

--

--

Alan Zhao
Alan Zhao

Written by Alan Zhao

Solutions Architect & Software Engineer

No responses yet