DEV Community

Cover image for Creating Amazon EC2 Instances: Console, CLI, and a Bastion Host or Jump Server 🖥️
Hashir Saud Khan
Hashir Saud Khan

Posted on

Creating Amazon EC2 Instances: Console, CLI, and a Bastion Host or Jump Server 🖥️

aws #ec2 #cli #tutorial

Creating Amazon EC2 Instances

INTRO
This lab is about the different ways you can launch an EC2 instance on AWS — through the console, and through the CLI. You'll launch one instance as a bastion host, then use that bastion host to launch a second instance (a web server) using the AWS CLI. By the end, you'll have a small real setup: one instance whose whole job is to give you a safe way in, and another instance doing the actual work.

Quick note before we start — if you've worked in on-prem networks or with other cloud providers, you might know the term jump server instead of bastion host. They're the same thing, just different names depending on where you learned it. A bastion host/jump server is a hardened instance that sits at the edge of your network, and it's the only way in — you connect to it first, then use it to reach other instances that aren't directly exposed. Same concept throughout this lab.

AWS Bastion Host

TASK 1: LAUNCH THE BASTION HOST FROM THE CONSOLE
First, you launch an EC2 instance through the AWS Console — this one becomes your bastion host.

Here's what you configure:

  • Name: Bastion host
  • AMI: Amazon Linux 2 (from Quick Start)
  • Instance type: t3.micro
  • Key pair: Proceed without a key pair — you'll use EC2 Instance Connect instead, so no key pair is needed
  • Network: Lab VPC, public subnet, public IP enabled
  • Security group: new one named Bastion security group, described as permitting SSH connections
  • Storage: default 8 GiB root volume
  • IAM instance profile: Bastion-Role — this gives the instance permission to make its own calls to EC2 later

Once everything's set, launch it.

Why we did this: You need a single, controlled entry point into your VPC before you do anything else. Instead of exposing every instance directly to the internet, you expose just this one — the bastion host — and everything else stays private, reachable only through it. The IAM role is the other half of this: without it, the bastion host would have no permission to talk to EC2 later in Task 3, no matter how you tried to connect to it.

TASK 2: CONNECT TO THE BASTION HOST
With the instance running, select it in the console, choose Connect, and use the EC2 Instance Connect tab to connect.

That's it — no key file, no terminal setup on your side. You're dropped straight into a shell on the bastion host, right from the browser.

Why we did this: This proves the bastion host is actually reachable and working before you rely on it to do anything else. It's also worth noticing how you connected — no SSH key was ever generated or downloaded for this instance. EC2 Instance Connect handles the authentication behind the scenes through IAM, which is a cleaner story than passing PEM files around, especially for a machine whose whole purpose is being your access point.

TASK 3: LAUNCH A WEB SERVER USING THE AWS CLI
Now for the real task — from inside that bastion host session, you use the AWS CLI to launch a second instance, this time as a web server. Unlike the console, the CLI doesn't guess anything for you — you have to supply every parameter yourself. So the first few steps are just about gathering those parameters.

Step 1 — Get the latest AMI ID:

AZ=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone`
export AWS_DEFAULT_REGION=${AZ::-1}
AMI=$(aws ssm get-parameters --names /aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2 --query 'Parameters[0].[Value]' --output text)
echo $AMI
Enter fullscreen mode Exit fullscreen mode

Instead of hardcoding an AMI ID (which goes stale the moment AWS patches it), you pull the current one from Parameter Store — the same Parameter Store you'd use to store any other config value.

Step 2 — Get the subnet ID:

SUBNET=$(aws ec2 describe-subnets --filters 'Name=tag:Name,Values=Public Subnet' --query Subnets[].SubnetId --output text)
echo $SUBNET
Enter fullscreen mode Exit fullscreen mode

Step 3 — Get the security group ID:

SG=$(aws ec2 describe-security-groups --filters Name=group-name,Values=WebSecurityGroup --query SecurityGroups[].GroupId --output text)
echo $SG
Enter fullscreen mode Exit fullscreen mode

Step 4 — Download the user data script:

wget https://aws-tc-largeobjects.s3.us-west-2.amazonaws.com/CUR-TF-100-RSJAWS-1-23732/171-lab-JAWS-create-ec2/s3/UserData.txt
cat UserData.txt
Enter fullscreen mode Exit fullscreen mode

This script installs the web server software and the web app itself, and it runs automatically the moment the instance boots — you never have to log into the new instance to set it up manually.

Step 5 — Launch the instance:

INSTANCE=$(aws ec2 run-instances \
  --image-id $AMI \
  --subnet-id $SUBNET \
  --security-group-ids $SG \
  --user-data file:///home/ec2-user/UserData.txt \
  --instance-type t3.micro \
  --tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=Web Server}]' \
  --query 'Instances[*].InstanceId' \
  --output text)
echo $INSTANCE
Enter fullscreen mode Exit fullscreen mode

Step 6 — Wait for it to be ready:

aws ec2 describe-instances --instance-ids $INSTANCE --query 'Reservations[].Instances[].State.Name' --output text
Enter fullscreen mode Exit fullscreen mode

Run this again until it says running instead of pending.

Step 7 — Test it:

aws ec2 describe-instances --instance-ids $INSTANCE --query Reservations[].Instances[].PublicDnsName --output text
Enter fullscreen mode Exit fullscreen mode

Paste that DNS name into a browser tab — if the web page loads, the instance launched, booted, and configured itself correctly, entirely through commands you typed.

Why we did this: This is the whole point of the CLI — repeatability. Everything you just did could be saved as a script and run again to spin up an identical web server anytime, with zero clicking. That's the real difference between the console and the CLI: the console is great for a one-off instance, but the CLI is what you reach for when you need the exact same setup twice, or twenty times, without relying on memory or a screenshot of the steps.

Web server

PICKING THE RIGHT METHOD
Worth remembering going forward:

  • Console — quick, one-off, temporary instances
  • CLI / scripts — repeatable, automatable, consistent deployments
  • CloudFormation — when you need multiple related resources launched and managed together as one unit

WHY THIS MATTERS OVERALL

  • Bastion host, jump server — same idea: one hardened, controlled entry point standing between the internet and everything else in your VPC
  • EC2 Instance Connect swaps out "manage an SSH key" for "let IAM handle it" — one less credential to lose track of
  • The CLI forces you to know every parameter that goes into launching an instance, which is exactly why it's worth learning even if the console feels faster day-to-day
  • Pulling the AMI ID from Parameter Store instead of hardcoding it means your launch script never silently uses an outdated, unpatched image

AWS #EC2 #BastionHost #JumpServer #CLI #CloudComputing

Top comments (0)