Ravindra BagaleCourses & study guides मराठी Track your progress

AWS SDK EC2 Examples: Launch, List, Stop and Terminate Instances (Python, PHP, Node.js)

We control EC2 servers from code: find the latest Amazon Linux image, launch a tagged instance, list instances with filters, wait until running, then stop, start and terminate — in Python, PHP and Node.js. This is what auto-start scripts and internal "spin up a test box" tools do.

What you'll learn in this chapter

  • Find the latest AMI from SSM
  • Launch an instance with tags
  • List instances with filters
  • Use waiters for running and stopped
  • Stop, start and terminate safely

1. The big picture: renting cars

Why do we need this?

Mei runs a car rental in Singapore. She picks a model (AMI), a size (instance type), puts a name sticker on it (tag), hands over the keys (key pair) and parks it in a lot (subnet). When the customer returns it she parks it (stop) or sells it (terminate). EC2 from code is the same list of choices.

What is it?

RunInstances launches; DescribeInstances lists; StopInstances, StartInstances and TerminateInstances change state. Tags are how scripts find "their" instances later.

How to do it (step by step)

Instance lifecycle from codeget AMI from SSM parameterRunInstances with tagswait instance_runningStopInstances / StartInstancesTerminateInstances
Stopped instances do not pay for compute, but EBS volumes still cost money.

2. Find the latest AMI

Why do we need this?

AMI IDs differ per region and change every month. Hard-coding one breaks next month.

What is it?

AWS publishes the latest Amazon Linux 2023 AMI as a public SSM parameter. Read it with GetParameter.

How to do it (step by step)

  1. Python.
import boto3
ssm = boto3.client("ssm", region_name="ap-southeast-1")
ami = ssm.get_parameter(Name="/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64")["Parameter"]["Value"]
print(ami)

Expected output:

ami-0a1b2c3d4e5f67890
  1. PHP.
<?php
require "vendor/autoload.php";
$ssm = new Aws\Ssm\SsmClient(["region" => "ap-southeast-1"]);
echo $ssm->getParameter(["Name" => "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"])["Parameter"]["Value"], PHP_EOL;

Expected output:

ami-0a1b2c3d4e5f67890
  1. Node.js.
import { SSMClient, GetParameterCommand } from "@aws-sdk/client-ssm";
const ssm = new SSMClient({ region: "ap-southeast-1" });
const r = await ssm.send(new GetParameterCommand({ Name: "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64" }));
console.log(r.Parameter.Value);

Expected output:

ami-0a1b2c3d4e5f67890

3. Launch a tagged instance and wait

Why do we need this?

Mei's booking app needs a test server for each new developer, named after them.

What is it?

Pass ImageId, InstanceType, MinCount/MaxCount and TagSpecifications. Then use the instance_running waiter and read the public IP.

How to do it (step by step)

  1. Python: run_instances and wait.
import boto3

ec2 = boto3.client("ec2", region_name="ap-southeast-1")
r = ec2.run_instances(
    ImageId="ami-0a1b2c3d4e5f67890", InstanceType="t3.micro", MinCount=1, MaxCount=1,
    TagSpecifications=[{"ResourceType": "instance", "Tags": [{"Key": "Name", "Value": "dev-mei"}, {"Key": "team", "Value": "booking"}]}],
)
iid = r["Instances"][0]["InstanceId"]
ec2.get_waiter("instance_running").wait(InstanceIds=[iid])
ip = ec2.describe_instances(InstanceIds=[iid])["Reservations"][0]["Instances"][0].get("PublicIpAddress")
print(iid, "running", ip)

Expected output:

i-0123456789abcdef0 running 13.250.10.20
  1. PHP: runInstances and waitUntil.
<?php
require "vendor/autoload.php";
$ec2 = new Aws\Ec2\Ec2Client(["region" => "ap-southeast-1"]);
$r = $ec2->runInstances([
    "ImageId" => "ami-0a1b2c3d4e5f67890", "InstanceType" => "t3.micro", "MinCount" => 1, "MaxCount" => 1,
    "TagSpecifications" => [["ResourceType" => "instance", "Tags" => [["Key" => "Name", "Value" => "dev-kunal"], ["Key" => "team", "Value" => "booking"]]]],
]);
$id = $r["Instances"][0]["InstanceId"];
$ec2->waitUntil("InstanceRunning", ["InstanceIds" => [$id]]);
echo "$id running", PHP_EOL;

Expected output:

i-0fedcba9876543210 running
  1. Node.js: RunInstancesCommand and waitUntilInstanceRunning.
import { EC2Client, RunInstancesCommand, waitUntilInstanceRunning } from "@aws-sdk/client-ec2";

const ec2 = new EC2Client({ region: "ap-southeast-1" });
const r = await ec2.send(new RunInstancesCommand({
  ImageId: "ami-0a1b2c3d4e5f67890", InstanceType: "t3.micro", MinCount: 1, MaxCount: 1,
  TagSpecifications: [{ ResourceType: "instance", Tags: [{ Key: "Name", Value: "dev-sophie" }, { Key: "team", Value: "booking" }] }],
}));
const id = r.Instances[0].InstanceId;
await waitUntilInstanceRunning({ client: ec2, maxWaitTime: 300 }, { InstanceIds: [id] });
console.log(id, "running");

Expected output:

i-0aa11bb22cc33dd44 running
  1. Verify: three dev boxes are running.
aws ec2 describe-instances --region ap-southeast-1 --filters Name=tag:team,Values=booking Name=instance-state-name,Values=running --query "Reservations[].Instances[].Tags[?Key=='Name'].Value[]" --output text

Expected output:

dev-mei	dev-kunal	dev-sophie

4. List instances with filters

Why do we need this?

At 7 pm a script should find every instance tagged team=booking to stop them and save money.

What is it?

Filters run on the AWS side (tag:key, instance-state-name, instance-type). Results are grouped in Reservations, so flatten them.

How to do it (step by step)

  1. Python: paginator plus filters.
import boto3

ec2 = boto3.client("ec2", region_name="ap-southeast-1")
flt = [{"Name": "tag:team", "Values": ["booking"]}, {"Name": "instance-state-name", "Values": ["running"]}]
ids = []
for page in ec2.get_paginator("describe_instances").paginate(Filters=flt):
    for res in page["Reservations"]:
        ids += [i["InstanceId"] for i in res["Instances"]]
print(len(ids), ids)

Expected output:

3 ['i-0123456789abcdef0', 'i-0fedcba9876543210', 'i-0aa11bb22cc33dd44']
  1. PHP: JMESPath search on the result.
<?php
require "vendor/autoload.php";
$ec2 = new Aws\Ec2\Ec2Client(["region" => "ap-southeast-1"]);
$r = $ec2->describeInstances(["Filters" => [["Name" => "tag:team", "Values" => ["booking"]], ["Name" => "instance-state-name", "Values" => ["running"]]]]);
print_r($r->search("Reservations[].Instances[].InstanceId"));

Expected output:

Array
(
    [0] => i-0123456789abcdef0
    [1] => i-0fedcba9876543210
    [2] => i-0aa11bb22cc33dd44
)
  1. Node.js: paginateDescribeInstances.
import { EC2Client, paginateDescribeInstances } from "@aws-sdk/client-ec2";

const ec2 = new EC2Client({ region: "ap-southeast-1" });
const Filters = [{ Name: "tag:team", Values: ["booking"] }, { Name: "instance-state-name", Values: ["running"] }];
const ids = [];
for await (const p of paginateDescribeInstances({ client: ec2 }, { Filters })) {
  for (const r of p.Reservations) ids.push(...r.Instances.map(i => i.InstanceId));
}
console.log(ids.length, ids);

Expected output:

3 [ 'i-0123456789abcdef0', 'i-0fedcba9876543210', 'i-0aa11bb22cc33dd44' ]

5. Stop, start and terminate

Why do we need this?

Stopping dev boxes 14 hours a day saves about 58% of their compute bill — for a team of 10 t3.medium boxes that is roughly $180 a month.

What is it?

Stop keeps the disk; start brings it back (new public IP). Terminate deletes the instance. DryRun=True checks permissions without doing anything.

How to do it (step by step)

  1. Python: dry run, then stop and wait.
import boto3
from botocore.exceptions import ClientError

ec2 = boto3.client("ec2", region_name="ap-southeast-1")
ids = ["i-0123456789abcdef0"]
try:
    ec2.stop_instances(InstanceIds=ids, DryRun=True)
except ClientError as e:
    print("DryRun:", e.response["Error"]["Code"])
ec2.stop_instances(InstanceIds=ids)
ec2.get_waiter("instance_stopped").wait(InstanceIds=ids)
print("stopped")

Expected output:

DryRun: DryRunOperation
stopped
  1. PHP: start again.
<?php
require "vendor/autoload.php";
$ec2 = new Aws\Ec2\Ec2Client(["region" => "ap-southeast-1"]);
$r = $ec2->startInstances(["InstanceIds" => ["i-0123456789abcdef0"]]);
echo $r["StartingInstances"][0]["PreviousState"]["Name"], " -> ", $r["StartingInstances"][0]["CurrentState"]["Name"], PHP_EOL;

Expected output:

stopped -> pending
  1. Node.js: terminate all booking dev boxes.
import { EC2Client, TerminateInstancesCommand, waitUntilInstanceTerminated } from "@aws-sdk/client-ec2";

const ec2 = new EC2Client({ region: "ap-southeast-1" });
const InstanceIds = ["i-0123456789abcdef0", "i-0fedcba9876543210", "i-0aa11bb22cc33dd44"];
const r = await ec2.send(new TerminateInstancesCommand({ InstanceIds }));
console.log(r.TerminatingInstances.map(i => i.CurrentState.Name));
await waitUntilInstanceTerminated({ client: ec2, maxWaitTime: 600 }, { InstanceIds });
console.log("terminated");

Expected output:

[ 'shutting-down', 'shutting-down', 'shutting-down' ]
terminated
  1. Verify: nothing tagged booking is running.
aws ec2 describe-instances --region ap-southeast-1 --filters Name=tag:team,Values=booking Name=instance-state-name,Values=running --query "length(Reservations)"

Expected output:

0

6. The same call in Java, Go, .NET and the CLI

Why do we need this?

Teams often mix languages. Once you know the call in Python, PHP and Node.js, the other SDKs are the same API with different syntax.

What is it?

All of them call the RunInstances API. Use the CLI line to test it quickly from a terminal.

How to do it (step by step)

StackCode
AWS CLIaws ec2 run-instances --image-id ami-x --instance-type t3.micro --count 1
Java v2ec2.runInstances(r -> r.imageId("ami-x").instanceType(InstanceType.T3_MICRO).minCount(1).maxCount(1));
Go v2client.RunInstances(ctx, &ec2.RunInstancesInput{ImageId: aws.String("ami-x"), InstanceType: types.InstanceTypeT3Micro, MinCount: aws.Int32(1), MaxCount: aws.Int32(1)})
.NETawait ec2.RunInstancesAsync(new RunInstancesRequest { ImageId = "ami-x", InstanceType = InstanceType.T3Micro, MinCount = 1, MaxCount = 1 });

7. Clean up so nothing keeps costing

Why do we need this?

Lab resources left running cost money and can be a security risk.

What is it?

Delete in reverse order: children first, then parents.

How to do it (step by step)

  1. Make sure no test instances are left.
aws ec2 describe-instances --region ap-southeast-1 --filters Name=tag:team,Values=booking Name=instance-state-name,Values=pending,running,stopped --query "length(Reservations)"

Expected output:

0

Ravindra Bagale's Tip

Tag everything at launch with TagSpecifications, not afterwards. Cost reports, stop-at-night scripts and IAM conditions all depend on tags.

8. Interview questions and answers

Q. How do you always get the newest Amazon Linux AMI in code?

Read the public SSM parameter /aws/service/ami-amazon-linux-latest/... with GetParameter.

Q. Why are instances inside Reservations?

One RunInstances call is one reservation that can hold many instances, so you flatten Reservations[].Instances[].

Q. What does DryRun do?

Checks permissions and parameters; success raises DryRunOperation, no change is made.

Q. Stop vs terminate?

Stop keeps the EBS disk and can start again; terminate deletes the instance.

Q. How do you wait until an instance is running?

Waiters: boto3 instance_running, PHP waitUntil("InstanceRunning"), Node waitUntilInstanceRunning.

Try at home

  1. Write a stop-at-night script in your favourite language that stops every running instance with tag autostop=yes.
  2. Print instance ID, type, state and Name tag as a table in all three languages.

In short

  1. AMI from the public SSM parameter.
  2. RunInstances with TagSpecifications.
  3. Filters + paginators to list.
  4. Waiters for running, stopped, terminated.
  5. DryRun to test permissions.

Practice Tasks

About this scenario

Patil Dairy is a dairy business in Kolhapur. Sunita Patil looks after its IT and cloud work. They count money in INR and run their AWS resources in the ap-south-1 Region. In the tasks below you do real jobs for them using what this lesson teaches. All names, account IDs, IP addresses and numbers are made up for practice.

0 / 12

Easy List your EC2 instances

Why: Before changing anything, see what is running and in which state.

What to do: Show details of all EC2 instances of Patil Dairy in ap-south-1.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy Reboot an instance

Why: A reboot keeps the same disks and IPs, unlike stop/start which may change the public IP.

What to do: Reboot the instance i-0b415a34440011a2b.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy Connect to an EC2 server

Why: SSH with a key file is the standard way to log in to a Linux EC2 instance.

What to do: Log in to the server 13.10.20.5 as ec2-user using the key patildairy-key.pem.

How:
  1. Find the Linux command that does this job.
  2. Add the options (like -r or -h) the task needs.
  3. Add the file names, folders or users in the right order.
  4. Use sudo if the job changes system files or users. Click Check answer.

Easy Stop an instance

Why: A stopped instance does not charge for compute, only for its disks.

What to do: Stop the instance i-0b415a34440011a2b.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Easy Start an instance

Why: Start it again in the morning when the team needs it.

What to do: Start the instance i-0b415a34440011a2b.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Create an AMI from a server

Why: An AMI is a full server image. Launch identical copies from it any time.

What to do: Create an AMI named patildairy-web-v1 from instance i-0b415a34440011a2b.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Allocate an Elastic IP

Why: An Elastic IP stays the same after stop/start, so DNS keeps working.

What to do: Allocate a new Elastic IP address for use in a VPC.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Attach the Elastic IP to the server

Why: Associating gives the server the fixed public IP that your domain points to.

What to do: Associate allocation eipalloc-004443a514bdd44 with instance i-0b415a34440011a2b.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Create a key pair

Why: You need a key pair to SSH into new Linux instances.

What to do: Create a key pair named patildairy-key.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Medium Launch one EC2 instance

Why: Launching from the CLI is repeatable and fast, and the same command works in scripts.

What to do: Launch 1 instance from AMI ami-0100cccbed1cfabcd, type t3.micro, key pair patildairy-key, security group sg-00044436514a4c3b2.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Show only running instances

Why: Filters ask AWS to send only what you need, which is faster than sorting by eye.

What to do: Describe only instances whose state is running.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Hard Launch an instance with a Name tag

Why: Tags at launch make billing and search easy from day one.

What to do: Launch 1 t3.small instance from ami-0100cccbed1cfabcd into subnet subnet-0004443a514bbb1, and tag the instance with Name=patildairy-web.

How:
  1. Read the task and decide which AWS service it is about.
  2. Write aws, then the service name, then the operation (the action word).
  3. Add each needed flag (--name) followed by its value from the task.
  4. Flags can be in any order. Click Check answer.

Revision, comparison and interview cards

Revise this lesson in a few minutes. Save the cards on your phone or download them as PNG files. The QR code on each card opens this lesson.

EC2 launch. AMI from public SSM parameter. RunInstances + TagSpecifications. Wait instance_running. Flatten Reservations[].Instances[].
Revision card: EC2 launch · Download PNG · Open card page
Stop, start, terminate. Filter by tag and state. DryRun checks permission. Stop keeps EBS; terminate deletes. Stopped still pays for disks.
Revision card: Stop, start, terminate · Download PNG · Open card page
Stop vs Terminate. EBS kept. / Instance deleted.
Comparison: Stop vs Terminate · Download PNG · Open card page
Why Reservations?. One RunInstances call = one reservation. It can hold many instances. Flatten before looping.
Interview card: Why Reservations? · Download PNG · Open card page

More cards: Ravi Sir's Cards.