Skip to main content

On This Page

Building Your First AWS Network with Terraform: 4 Failures That Teach More Than Success

3 min read
Share

These articles are AI-generated summaries. Please check the original sources for full details.

Week 1 of a five-week Infrastructure-as-Code journey, told honestly: the 403s, the 400s, the missing resource, and the security group I got wrong twice.

A developer set out to provision an 8-resource AWS network using Terraform in Week 1 of a structured IaC course. Every single resource failed on the first attempt, resulting in 4 distinct failure modes before the stack ran correctly.

Why This Matters

Infrastructure-as-Code promises deterministic, repeatable deployments, but the reality is that platform-specific quirks (account policies, free-tier restrictions, resource defaults) and semantic misunderstandings (public subnet naming vs. routing, security group egress defaults) cause the vast majority of provisioning failures. In this case, 4 out of 8 resources failed on the first attempt — a 50% failure rate — not due to syntax, but due to mismatches between developer assumptions and AWS/Terraform behavior, highlighting that cloud debugging is fundamentally about reading error layers, not writing code.

Key Insights

  • 403 vs. 400 errors represent different failure layers: 403 means AWS knows the identity but denies permission (e.g., missing IAM policy for ec2:DescribeImages), 400 means the request itself is invalid (e.g., t2.micro not free-tier eligible in ca-central-1); distinguishing them halves debugging time (AWS error semantics, 2026).
  • AWS free-tier eligibility varies by region and account age — t3.micro works in ca-central-1 where t2.micro does not; the aws ec2 describe-instance-types --filters 'Name=free-tier-eligible,Values=true' command surfaces available types (AWS documentation, 2026).
  • Terraform’s aws_security_group resource removes the default ‘allow all outbound’ rule the moment it manages the group — any missing egress block results in an instance that cannot reach the internet at all (Terraform provider behavior, 2026).
  • A subnet is public only when a route table with a 0.0.0.0/0 → internet gateway route is explicitly associated with it; forgetting the route table association silently falls back to the VPC’s main route table, which has no internet path (AWS VPC documentation, 2026).

Working Examples

Boot script that installs Apache web server and deploys a test HTML page on an Amazon Linux EC2 instance. Included in the EC2 user_data field to automatically serve content when the instance launches.

user_data = <<-EOF
#!/bin/bash
dnf install -y httpd
echo "<h1>Deployed with Terraform</h1>" > /var/www/html/index.html
systemctl enable --now httpd
EOF

Practical Applications

  • Use aws sts get-caller-identity before any Terraform apply when juggling multiple AWS accounts to confirm which identity is active — the developer’s 403 persisted because CLI was authenticated to Account B while policies were attached in Account A.
  • Parameterize instance_type as a Terraform variable and override via terraform apply -var='instance_type=t3.micro' to test free-tier-compatible types without modifying source code — avoids touching code for region-specific instance eligibility.
  • Always include explicit egress rules in Terraform security groups — AWS defaults allow all outbound, but Terraform’s managed resource removes that default, silently blocking outbound connections (e.g., OS updates, outbound APIs).
  • Verify a ‘public’ subnet by checking its route table association, not its name — the developer’s subnet was named ‘public’ but used the VPC’s main route table (no internet gateway route), causing the EC2 to be unreachable despite correct security group rules.

References:

Continue reading

Next article

"Real Players Win Only 25% of Matches Against Bots": Developer Uncovers Unintended Bot Difficulty in Economic Card Game

Related Content