Bash and AWS CLI are your go-to tools for automating AWS tasks, but the real magic isn’t in scripting simple aws s3 ls commands; it’s in how you orchestrate complex workflows and manage state.

Let’s say you need to spin up a new EC2 instance, configure it with some user data, and then immediately attach an Elastic IP. Here’s how you might do it:

#!/bin/bash

# Define variables
INSTANCE_NAME="my-automated-instance"
AMI_ID="ami-0abcdef1234567890" # Example AMI ID for Amazon Linux 2
INSTANCE_TYPE="t2.micro"
SECURITY_GROUP_ID="sg-0123456789abcdef0"
KEY_PAIR_NAME="my-ssh-key"
REGION="us-east-1"
USER_DATA_FILE="bootstrap.sh"

# 1. Launch the EC2 instance
echo "Launching EC2 instance..."
INSTANCE_INFO=$(aws ec2 run-instances \
    --image-id "$AMI_ID" \
    --instance-type "$INSTANCE_TYPE" \
    --security-group-ids "$SECURITY_GROUP_ID" \
    --key-name "$KEY_PAIR_NAME" \
    --region "$REGION" \
    --tag-specifications "ResourceType=instance,Tags=[{Key=Name,Value=$INSTANCE_NAME}]" \
    --user-data file://"$USER_DATA_FILE" \
    --query 'Instances[0].InstanceId' \
    --output text)

if [ -z "$INSTANCE_INFO" ]; then
    echo "Error: Failed to launch EC2 instance."
    exit 1
fi
echo "Instance launched with ID: $INSTANCE_INFO"

# 2. Wait for the instance to be running
echo "Waiting for instance to enter 'running' state..."
aws ec2 wait instance-running --instance-ids "$INSTANCE_INFO" --region "$REGION"
echo "Instance is now running."

# 3. Allocate a new Elastic IP address
echo "Allocating Elastic IP address..."
ALLOCATION_ID=$(aws ec2 allocate-address \
    --domain vpc \
    --region "$REGION" \
    --query 'AllocationId' \
    --output text)

if [ -z "$ALLOCATION_ID" ]; then
    echo "Error: Failed to allocate Elastic IP."
    # Clean up the instance if EIP allocation fails
    aws ec2 terminate-instances --instance-ids "$INSTANCE_INFO" --region "$REGION"
    exit 1
fi
echo "Allocated Elastic IP with Allocation ID: $ALLOCATION_ID"

# 4. Associate the Elastic IP with the instance
echo "Associating Elastic IP with instance $INSTANCE_INFO..."
aws ec2 associate-address \
    --instance-id "$INSTANCE_INFO" \
    --allocation-id "$ALLOCATION_ID" \
    --region "$REGION" \
    --output text

echo "Elastic IP associated successfully."

echo "Instance setup complete."

This script demonstrates a common pattern: launch a resource, wait for it to become available, then perform subsequent actions. The aws ec2 wait instance-running command is crucial here; it polls AWS until the instance state is running, preventing your script from trying to associate an EIP with an instance that isn’t ready. The --query and --output text flags are essential for extracting specific pieces of information (like the InstanceId or AllocationId) that you’ll need for subsequent commands.

The mental model for automating AWS with Bash and CLI is about building predictable, idempotent workflows. You define inputs (like AMI IDs, instance types, security groups), execute commands to create or modify resources, and then use aws ec2 wait or similar commands to synchronize your script’s progress with AWS’s state. Error handling is paramount: check the exit codes of aws commands and the output for success indicators. If a step fails, decide whether to halt, retry, or attempt cleanup.

A common, yet often overlooked, aspect of state management is handling the cleanup of allocated resources like Elastic IPs. If your script fails after allocating an EIP but before associating it, that EIP will sit there, incurring charges. A robust script needs to track allocation IDs and have a cleanup routine that releases them if they aren’t associated. For instance, you might store the ALLOCATION_ID in a temporary file and have a trap command to execute a cleanup function when the script exits for any reason.

The next logical step in automation is often managing more dynamic configurations, like using AWS Systems Manager Parameter Store or Secrets Manager to inject sensitive information or configuration settings into your instances at launch or runtime.

Want structured learning?

Take the full Bash course →