Skip to content

Script to list Terminated EC2 instances in AWS

  • by

AWS doesn’t provide stats on list of terminated EC2 instances across a single account or across every accounts in an organization. To get the list of terminated instances, we may need to use boto3 script and use EC2 instance client to get the list of terminated instances as shown below

Contents

List of Terminated instances for a single account

Here is the Python boto3 script that will get list of terminated instances for a single account. You can execute this script from an EC2 instance that has the required IAM Role

import boto3
import csv

# Initialize EC2 client
ec2 = boto3.client('ec2')

# Describe instances with 'terminated' state
response = ec2.describe_instances(
    Filters=[
        {
            'Name': 'instance-state-name',
            'Values': ['terminated']
        }
    ]
)

# Prepare CSV file
with open('terminated_instances.csv', mode='w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(['InstanceId', 'LaunchTime', 'State'])

    # Extract and write terminated instance details
    for reservation in response['Reservations']:
        for instance in reservation['Instances']:
            instance_id = instance['InstanceId']
            launch_time = instance['LaunchTime']
            state = instance['State']['Name']
            writer.writerow([instance_id, launch_time, state])
            print(f"ID: {instance_id}, Launched: {launch_time}, State: {state}")

print("\n✅ Terminated instance details written to 'terminated_instances.csv'")

Terminated instances for every account in the organization

The below script will get you the list of instances for every account in the organization and this script needs to be executed from a master account or by using a role that has access to every account across the organization

import boto3
import csv
from datetime import datetime

# Role to assume in each account
ROLE_NAME = 'OrganizationAccountAccessRole'  # Replace if using a custom role name

# Output CSV file
csv_file = 'terminated_instances_by_account.csv'

# Initialize Organizations client in master account
org_client = boto3.client('organizations')

# Get list of accounts
accounts = org_client.list_accounts()['Accounts']

# Prepare CSV file
with open(csv_file, mode='w', newline='') as file:
    writer = csv.writer(file)
    writer.writerow(['AccountId', 'AccountName', 'InstanceId', 'InstanceName', 'LaunchTime', 'TerminationTime'])

    for account in accounts:
        account_id = account['Id']
        account_name = account['Name']

        # Skip suspended accounts
        if account['Status'] != 'ACTIVE':
            continue

        # Assume role into target account
        sts_client = boto3.client('sts')
        try:
            assumed_role = sts_client.assume_role(
                RoleArn=f'arn:aws:iam::{account_id}:role/{ROLE_NAME}',
                RoleSessionName='TerminatedInstanceAudit'
            )
        except Exception as e:
            print(f"❌ Could not assume role in account {account_id} ({account_name}): {e}")
            continue

        creds = assumed_role['Credentials']
        ec2 = boto3.client(
            'ec2',
            aws_access_key_id=creds['AccessKeyId'],
            aws_secret_access_key=creds['SecretAccessKey'],
            aws_session_token=creds['SessionToken']
        )

        # Get terminated instances
        try:
            response = ec2.describe_instances(
                Filters=[{'Name': 'instance-state-name', 'Values': ['terminated']}]
            )
        except Exception as e:
            print(f"⚠️ Error fetching EC2 data for account {account_id}: {e}")
            continue

        for reservation in response['Reservations']:
            for instance in reservation['Instances']:
                instance_id = instance['InstanceId']
                launch_time = instance['LaunchTime']
                state_transition_reason = instance.get('StateTransitionReason', '')
                termination_time = None

                # Extract termination time from state transition reason
                if 'terminated at' in state_transition_reason:
                    try:
                        termination_time = state_transition_reason.split('terminated at ')[-1]
                        termination_time = datetime.strptime(termination_time, '%Y-%m-%dT%H:%M:%S.%fZ')
                    except:
                        termination_time = None

                # Extract Name tag
                name_tag = ''
                for tag in instance.get('Tags', []):
                    if tag['Key'] == 'Name':
                        name_tag = tag['Value']
                        break

                writer.writerow([
                    account_id,
                    account_name,
                    instance_id,
                    name_tag,
                    launch_time,
                    termination_time if termination_time else 'Unknown'
                ])

        print(f"✅ Processed account {account_id} ({account_name})")

print(f"\n📄 All terminated instance data written to '{csv_file}'")
Tags: