AWS Session Manager: An Alternative to Access Amazon EC2 Instance

Luthfi Anandra
AWS in Plain English
7 min readOct 1, 2021

--

For Amazon Web Services (AWS) users, the most common way to access to Amazon EC2 instance is using SSH to EC2 IP Address. That’s the case whether the EC2 has a public IP and we SSH to that public IP, or the instance is not associated with public IP, by having an SSH connection through a bastion host, then connecting to an EC2 instance in a private network.

But do you know that there is another method to access EC2 instances without SSH? Maybe some people know this method and are using it on daily basis. But for someone who’s not familiar, to access EC2 instance, we can use AWS Session Manager as an alternative method.

Session Manager

AWS Session Manager is a feature by AWS that allows us to manage EC2 instance, on-premise instance, or virtual machine (VM) from interactive browser-based shell or via AWS Command Line Interface (AWS CLI). Session Manager allows secure and auditable instance management without any necessity to open an inbound port, using bastion host or using an SSH key.

Below are some benefits of Session Manager (refer to the AWS documentation above):

  • Centralized access control to instances using IAM policies
  • No open inbound ports and no need to manage bastion hosts or SSH keys
  • One-click access to instances from the console and CLI
  • Port forwarding
  • Cross-platform support for Windows, Linux, and macOS
  • Logging and auditing session activity

In this article, I will explain the basics of AWS Session Manager and how to configure it. I will create the IAM user that acts as ssm-user in Notebook/Mac, then I will create the IAM role that acts as EC2 IAM Role/IAM Instance Profile. These IAM users and IAM associate with their own IAM policies and have different privileges.

IAM is configured using Infrastructure as Code (IaC) tool which in this article is Terraform (Terraform Cloud) and for EC2 configurations — I will not explain this in further detail here.

If you are interested to know how to configure EC2 instance using Terraform, please check out my other blog post that discusses it on this link.

Configuration

Before we begin configuration, I will explain the directory tree structure used in this article below:

iam/
├─ main.tf
├─ policies.tf
├─ roles.tf
├─ users.tf
├─ variables.tf
├─ json-policies/
├─ ludes-ec2-admin.json
├─ ludes-ssm-user.json
  1. The first step: I start with creating 1 file named main.tf. In this file, I will declare terraform configuration that will refer to terraform cloud (app.terraform.io) as a backend. I also define the organization and workspace name. After that, I declare terraform provider that was used which is hashicorp/aws and its version. In the end, I declare AWS region that refers to variable aws_region declared on variable.tf.
# Setup terraform cloud and workspace
terraform {
backend "remote" {
hostname = "app.terraform.io"
organization = "lanandra"

workspaces {
name = "iam"
}
}
}

# Setup terraform providers
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 3.57.0"
}
}

required_version = ">= 1.0.6"
}

# Setup AWS provider
provider "aws" {
region = var.aws_region
}

2. Next I will explain the file variable.tf. In this file, I declared the variable aws_region as I mentioned in the previous step.

# Setup AWS Region
variable "aws_region" {
type = string
description = "AWS Region"
default = "ap-southeast-1"
}

3. Next I will create iam_user in file users.tf. In this file, I created 1 IAM user named ludes-ssm-user.

# Configuration related to IAM users

resource "aws_iam_user" "ludes_ssm_user" {
name = "ludes-ssm-user"
path = "/"
force_destroy = true

tags = {
"Name" = "ludes-ssm-user"
}
}

4. Next step is, I create EC2 IAM Role/IAM Instance Profile as in file roles.tf. In this file, I start with creating assume role policy for EC2 access using data argument as reference. Then I create an IAM role named ludes-ec2-admin which assumes the role is referred from the data argument above that has been converted to JSON. And last, I create aws_iam_instance_profile with a similar name referred to ludes-ec2-admin role because I want this role to act as EC2 IAM Role/IAM Instance Profile.

# IAM Policy with assume role to EC2
data "aws_iam_policy_document" "ec2_assume_role" {
statement {
actions = ["sts:AssumeRole"]

principals {
type = "Service"
identifiers = ["ec2.amazonaws.com"]
}
}
}

# Configure IAM role
resource "aws_iam_role" "ludes_ec2_admin" {
name = "ludes-ec2-admin"
path = "/"
assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json
}

# Configure IAM instance profile
resource "aws_iam_instance_profile" "ludes_ec2_admin" {
name = "ludes-ec2-admin"
role = aws_iam_role.ludes_ec2_admin.name
}

5. Next, I will define the IAM policy that is used by user and role. For ludes-ssm-user, I create inline policy for that user and policy file referred to file /json-policies/ludes-ssm-user.json. And for EC2 IAM Role ludes-ec2-admin, I create inline policy referred to file /json-policies/ludes-ec2-admin.json

# Configuration related to IAM policies

resource "aws_iam_user_policy" "ludes_ssm_user" {
name = "ludes-ssm-user"
user = aws_iam_user.ludes_ssm_user.name

policy = file("./json-policies/ludes-ssm-user.json")
}

resource "aws_iam_role_policy" "ludes_ec2_admin" {
name = "ludes-ec2-admin"
role = aws_iam_role.ludes_ec2_admin.id

policy = file("./json-policies/ludes-ec2-admin.json")
}

6. Below are the details for each JSON policy.

ludes-ssm-user.json

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:ResumeSession",
"ssm:TerminateSession",
"ssm:StartSession",
"ssm:DescribeSessions",
"ssm:GetConnectionStatus"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances"
],
"Resource": "*"
}
]
}

ludes-ec2-admin.json

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:DescribeAssociation",
"ssm:GetDeployablePatchSnapshotForInstance",
"ssm:GetDocument",
"ssm:DescribeDocument",
"ssm:GetManifest",
"ssm:GetParameter",
"ssm:GetParameters",
"ssm:ListAssociations",
"ssm:ListInstanceAssociations",
"ssm:PutInventory",
"ssm:PutComplianceItems",
"ssm:PutConfigurePackageResult",
"ssm:UpdateAssociationStatus",
"ssm:UpdateInstanceAssociationStatus",
"ssm:UpdateInstanceInformation"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ssmmessages:CreateControlChannel",
"ssmmessages:CreateDataChannel",
"ssmmessages:OpenControlChannel",
"ssmmessages:OpenDataChannel"
],
"Resource": "*"
},
{
"Effect": "Allow",
"Action": [
"ec2messages:AcknowledgeMessage",
"ec2messages:DeleteMessage",
"ec2messages:FailMessage",
"ec2messages:GetEndpoint",
"ec2messages:GetMessages",
"ec2messages:SendReply"
],
"Resource": "*"
}
]
}

7. Next step is running terraform plan and terraform apply via Terraform Cloud. But I will not describe more details in this article.

8. After terraform apply has finished and resource successfully created. Then we can verify it from AWS Web Console. I will verify ludes-ssm-user user and ludes-ec2-admin role has been successfully created.

9. As I mentioned earlier in this article, I have prepared 1 EC2 instance that will be act as a target for Session Manager. At first, I will run command aws ec2 describe-instances from local notebook/mac to verify the instance ID using ludes-ssm-user profile. I also have configured some aws cli configuration using command aws configure — profile ludes-ssm-user and input the related AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY.

aws ec2 describe-instances --filters Name=instance-state-name,Values=running \
--query 'Reservations[*].Instances[*].{Instance:InstanceId,Name:Tags[?Key==`Name`]|[0].Value}' \
--profile ludes-ssm-user \
--output text

10. Expected result when running above command is, aws cli will display the instance ID of EC2 .

11. We also can verify it from AWS Web Console to make sure the instance ID is correct.

12. Next we try to connect/access the EC2 using the Session Manager.

Use this command to start session:

aws ssm start-session --target [instance_id] --profile [aws_cli_profile]

13. From the screenshot above, we can verify that the EC2 connection/session has been established and we can run several Linux commands. Also, we can verify that access is using without SSH or bastion host.

Conclusion

So we have reached the end of this article. In this article, we have discussed what is AWS Session Manager, how it works, and its benefit. Also, we have discussed that to access an EC2 instance, we can do it without SSH key or via Bastion Host but via Session Manager.

We have also simulated the basic configuration of AWS Session Manager.

If you have any suggestions, critiques or comments, please leave them in the comment section.

Hope this article will benefit you. Thank you.

More content at plainenglish.io

--

--

Site Reliability Engineer | AWS Community Builder | HashiCorp Ambassador