Terraform code to quickly spin up / tear down an EC2 instance in AWS. A persistent EBS volume is also created and mounted at /home
.
While it is possible to start and stop EC2 instances and thus only pay for the compute you use, it is generally a good idea to be able to terminate and launch fresh instances. One way to do preserve your work is to decouple your home directory from the EC2 instance by mounting a separate EBS volume configured not to be destroyed on termination.
Ensure you have a public key (for example in ~/.ssh/id_rsa.pub
). If not, you can create one with:
ssh-keygen
Create a terraform.tfvars
file:
region = "eu-west-2" # AWS region
instance_type = "t2.micro" # EC2 instance type
volume_size = 20 # Size of home EBS volume in GB
public_key_path = "~/.ssh/id_rsa.pub" # Path to public key
ingress_ports = [22] # Ports to open
# Commands to run on startup (e.g. to install pip)
startup_commands = [
<<-EOL
apt-get update
apt-get install -y python3-pip
EOL
]
Then run:
terraform init
terraform apply -auto-approve
and take a note of the public IP address and ID of the instance. (You can recover these at any time with terraform output
.) Alternatively, you can run ./create.sh <instance_type>
.
You can then SSH into the instance with
ssh ubuntu@<public_ip>
or simply ./connect.sh
.
You can terminate the instance from the command line by running:
aws ec2 terminate-instances --instance-ids <instance_id>
aws ec2 wait instance-terminated --instance-ids <instance_id>
or ./terminate.sh
.
If you re-run the apply or ./create.sh
command, Terraform will create a new instance with a new public IP address and attach the existing EBS volume to it. Note that your home EBS volume will persist and you will be charged $0.10 per GB-month whether or not it is attached to an EC2 instance (until you run terraform destroy -auto-approve
).
To debug issues with the startup_commands
, you can SSH into the instance and inspect the output from tail -f /var/log/cloud-init-output.log
. Bear in mind that the script may still be running in the background. To wait for the script to finish run cloud-init status --wait
.
To increase your the size of your EBS volume without destroying your data, first change the volume_size
variable in terraform.tfvars
, run terraform apply -auto-approve
and connect to your instance. In the instance, run:
if [ -e /dev/xvda ]; then
sudo resize2fs /dev/xvda
else
sudo resize2fs /dev/nvme1n1
fi
You can then run df -h /home
to check that the volume has been resized.
You can, of course, tunnel to any port via SSH on port 22 with
ssh -L <local_port>:localhost:<remote_port> ubuntu@<public_ip>
but, if you want to open ports to the public, just set the variable
ingress_ports = [22, 80, 443] # Ports to open
to include the ports you want to make accessible (for example to run a web server on 80 and 443).
If you plan to always install the same packages every time you spin up an instance, you can create your own AMI with the packages pre-installed by running the following commands:
instance_id=$(terraform output -json | jq -r '.instance_id.value')
aws ec2 stop-instances --instance-ids $instance_id
aws ec2 wait instance-stopped --instance-ids $instance_id
# Detach home EBS volume, otherwise a snapshot will be created
volume_id=$(terraform output -json | jq -r '.home_ebs_volume.value')
aws ec2 detach-volume --volume-id $volume_id
aws ec2 wait volume-available --volume-id $volume_id
image_id=$(aws ec2 create-image --instance-id $instance_id --name "my_ami" --query 'ImageId' --output text)
aws ec2 wait image-available --image-ids $image_id
Then add the following lines to terraform.tfvars
to select your AMI next time you spin up an instance:
ami_owner = "self" # Owner of AMI
ami_name = "my_ami" # Name of AMI