I initially planned to deploy my website to an EC2 instance. I've used Linode in the past for small deployments like this, but it probably behooves me to get more familiar with the industry standard. Funnily enough this is coming off the cusp of a major outage in us-east-1. Post-mortem here.

Leaning into the clichéd take, it is indeed disturbing that so many services folded at this single point of failure. On the other hand, it can be liberating as IT guy to shrug and say "AWS issue" when all hell breaks loose in the office. The most amusing effect of an IaaS/SaaS outage I've seen was one that impacted my company's VoIP provider at the time. During the downtime, we were inundated with ghost calls on our office phones, my supposition being that an upstream network misconfiguration caused internet traffic/noise to be incorrectly routed to our phones. I ended up disconnecting the possessed PoE devices until our vendor had resolved the issue.
Getting back on topic, I figured I'd proceed with automating the EC2 deployment with Terraform, a provisioning automation tool I've used before with Linodes and Azure. To further show how in tune I am with the times, I asked my friend Claude for some help with this simple use case. I need to provision a minimal EC2 instance with a Network Security Group configured to allow inbound HTTP/HTTPS traffic.
# Claude generated code below
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "us-east-1" # Change to your desired region
}
# Security Group
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for HTTP/HTTPS traffic"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
tags = {
Name = "web-sg"
}
}
resource "aws_instance" "web" {
ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2 AMI (us-east-1)
instance_type = "t3.micro"
vpc_security_group_ids = [aws_security_group.web.id]
associate_public_ip_address = true
tags = {
Name = "web-server"
}
}
The provider version is a bit behind from the latest, understandable as version 6 was released just a few months ago.
I took a quick gander at the online documentation for each aws resource. Looking at the doc for aws_security_group:
"Avoid using the ingress and egress arguments of the aws_security_group resource to configure in-line rules, as they struggle with managing multiple CIDR blocks, and, due to the historical lack of unique IDs, tags and descriptions. To avoid these problems, use the current best practice of the aws_vpc_security_group_egress_rule and aws_vpc_security_group_ingress_rule resources with one CIDR block per rule."
Alrighty then. I'm also going to revise the aws_instance block to provision an Ubuntu Server instance vs Amazon Linux (no technical reason, just an arbitrary preference). I'll query for the latest 24.04 image. With these revisions (also going to add rules for IPv6):
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 6.18"
}
}
}
provider "aws" {
region = "us-east-1"
}
data "aws_ami" "ubuntu" {
most_recent = true
filter {
name = "name"
values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server-*"]
}
owners = ["099720109477"] # Canonical
}
resource "aws_security_group" "web" {
name = "web-sg"
description = "Security group for public HTTP/HTTPS traffic"
}
resource "aws_vpc_security_group_ingress_rule" "web_ports_ipv4" {
# I added a loop here to save myself from two separate blocks for each protocol
for_each = {
http = 80
https = 443
}
security_group_id = aws_security_group.web.id
from_port = each.value
to_port = each.value
ip_protocol = "tcp"
cidr_ipv4 = "0.0.0.0/0"
description = "${each.key}-inbound-ipv4"
}
resource "aws_vpc_security_group_ingress_rule" "web_ports_ipv6" {
for_each = {
http = 80
https = 443
}
security_group_id = aws_security_group.web.id
from_port = each.value
to_port = each.value
ip_protocol = "tcp"
cidr_ipv6 = "::0/0"
description = "${each.key}-inbound-ipv6"
}
resource "aws_vpc_security_group_egress_rule" "all_ipv4" {
security_group_id = aws_security_group.web.id
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"
description = "allow-all-outbound-ipv4"
}
resource "aws_vpc_security_group_egress_rule" "all_ipv6" {
security_group_id = aws_security_group.web.id
ip_protocol = "-1"
cidr_ipv6 = "::0/0"
description = "allow-all-outbound-ipv4"
}
resource "aws_instance" "webserver" {
ami = data.aws_ami.ubuntu.id
instance_type = "t2.micro"
vpc_security_group_ids = [aws_security_group.web.id]
associate_public_ip_address = true
tags = {
Name = "websrv"
}
}
❯ terraform validate
Success! The configuration is valid.
This will do for now, time to let it rip:
❯ terraform apply
...
╷
│ Error: creating EC2 Instance: operation error EC2: RunInstances, https response error StatusCode: 400, RequestID: 6f7f7430-9d70-445f-9d0b-46559b4e280a, api error Blocked: This account is currently blocked and not recognized as a valid account. Please contact https://support.console.aws.amazon.com/support/home?region=us-east-1#/case/create?issueType=customer-service&serviceCode=account-management&categoryCode=account-verification if you have questions.
│
│ with aws_instance.webserver,
│ on main.tf line 83, in resource "aws_instance" "webserver":
│ 83: resource "aws_instance" "webserver" {
│
╵
Uh... maybe they didn't like me talking about the outage earlier?
I've had this particular AWS account for years without ever using it all that much. Maybe, like a car in winter, I needed to keep my AWS account warm for it to keep working. Definitely not an IAM roles/permissions issue either; I'm not able to spin up an EC2 with my root account. Ironically, I can't shrug and say "AWS issue" for this one, unless I want to say it to myself in the mirror.
I've done all I can do for now and opened a support case with Amazon. I'm sure one of their many employees will be right on this-
Exclusive: Amazon targets as many as 30,000 corporate job cuts, sources say | Reuters
Oof. Guess it's back to a small VPS like linode. In all honesty, this is probably way more appropriate for my use small case. Small VPS's have very clear-cut, unambiguous pricing, and I don't foresee myself needing to scale way up anytime soon. A petabyte scale data warehouse in Redshift is likely overkill for some 10 KB blog posts.
If you'd like to see my Terraform config for deploying my Linode servers, you can check it out here.
Until next time - Div