Initial commit

This commit is contained in:
2026-08-19 12:46:34 +01:00
commit 88b020837b
6 changed files with 532 additions and 0 deletions
+26
View File
@@ -0,0 +1,26 @@
# Terraform
.terraform/
*.tfstate
*.tfstate.*
*.tfplan
crash.log
crash.*.log
# Variable files may contain secrets; commit only examples
*.tfvars
*.tfvars.json
!*.tfvars.example
# Local overrides
override.tf
override.tf.json
*_override.tf
*_override.tf.json
.terraformrc
terraform.rc
# Superseded RTF originals
*.rtf
# macOS
.DS_Store
+45
View File
@@ -0,0 +1,45 @@
# This file is maintained automatically by "terraform init".
# Manual edits may be lost in future updates.
provider "registry.terraform.io/hashicorp/aws" {
version = "5.100.0"
constraints = "~> 5.0"
hashes = [
"h1:Ijt7pOlB7Tr7maGQIqtsLFbl7pSMIj06TVdkoSBcYOw=",
"zh:054b8dd49f0549c9a7cc27d159e45327b7b65cf404da5e5a20da154b90b8a644",
"zh:0b97bf8d5e03d15d83cc40b0530a1f84b459354939ba6f135a0086c20ebbe6b2",
"zh:1589a2266af699cbd5d80737a0fe02e54ec9cf2ca54e7e00ac51c7359056f274",
"zh:6330766f1d85f01ae6ea90d1b214b8b74cc8c1badc4696b165b36ddd4cc15f7b",
"zh:7c8c2e30d8e55291b86fcb64bdf6c25489d538688545eb48fd74ad622e5d3862",
"zh:99b1003bd9bd32ee323544da897148f46a527f622dc3971af63ea3e251596342",
"zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425",
"zh:9f8b909d3ec50ade83c8062290378b1ec553edef6a447c56dadc01a99f4eaa93",
"zh:aaef921ff9aabaf8b1869a86d692ebd24fbd4e12c21205034bb679b9caf883a2",
"zh:ac882313207aba00dd5a76dbd572a0ddc818bb9cbf5c9d61b28fe30efaec951e",
"zh:bb64e8aff37becab373a1a0cc1080990785304141af42ed6aa3dd4913b000421",
"zh:dfe495f6621df5540d9c92ad40b8067376350b005c637ea6efac5dc15028add4",
"zh:f0ddf0eaf052766cfe09dea8200a946519f653c384ab4336e2a4a64fdd6310e9",
"zh:f1b7e684f4c7ae1eed272b6de7d2049bb87a0275cb04dbb7cda6636f600699c9",
"zh:ff461571e3f233699bf690db319dfe46aec75e58726636a0d97dd9ac6e32fb70",
]
}
provider "registry.terraform.io/hashicorp/random" {
version = "3.9.0"
hashes = [
"h1:OO+IuvQJSPmWdN8AyyIEvPJbLvDQpgX/zbktoa9KsJE=",
"zh:161ad0bd9a75768c82f53fb6e7172a9d8be2d4889b012645a34795031aaf1bf1",
"zh:19dc9a5b17729725ccfc4f45b0500af0ee5bc6b6b160c7adb8f2bf617d2c80ea",
"zh:269eda8fe42daa7974d5a34d166c3ba9defe80cde86c01e4dadcfdf2e1f05e5f",
"zh:373f7c65566f8f2cc7f45d698654feb9d988996957e1266a69ca00c52d6d16d0",
"zh:5599d16804c41c83009ec621b6d6b6f74e102f5827678a4750f8809055546b61",
"zh:583be0440469a22bff70dcfa56593b01566860b29607437264adb51060cf46fc",
"zh:5f211d8ec3f2e1f414870d9584bfe26e6995560ef81c748f8447a48164767398",
"zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3",
"zh:7b547fd16216761ef86efc3ed516ac5ac0c5c42b7c7eb24a08cef2d93f69ed5e",
"zh:7e7c0679daf2a382151d05068c8c3f0dae6b7b7dccf818827b73dd08638df2ef",
"zh:8089dec888a8038b9b4fb23b3df7e1057293dbc5b60b42cc47ff690d69d4b61b",
"zh:c51f15a031edfd6f23ce8ced3446ca7f8d8d647e2499890d7d5d10d5016d7257",
"zh:c94784f005708890dc6895afd53636ec00ec1e430b15d41e5aebfb1d4b39bd04",
]
}
+56
View File
@@ -0,0 +1,56 @@
# hello-world-tf
Minimal AWS network and storage baseline managed with Terraform. Creates a VPC with public and private subnets, an internet gateway, an optional NAT gateway, and a private S3 bucket. No compute resources.
## What it creates
- **VPC** — `10.0.0.0/16` by default, DNS support and hostnames enabled
- **2 public subnets** (`10.0.1.0/24`, `10.0.2.0/24`) — auto-assign public IPs, routed to the internet gateway
- **2 private subnets** (`10.0.10.0/24`, `10.0.20.0/24`) — own route table, no internet egress unless the NAT gateway is enabled
- **Internet gateway** + public route table
- **NAT gateway** (optional, off by default) — single NAT in the first public subnet with an Elastic IP; adds a default route for the private subnets
- **S3 bucket** — `<project>-<env>-app-data-<random-suffix>`, versioning on by default, all public access blocked
All resources are tagged with `Project`, `Environment`, `ManagedBy`, and `CreatedBy` via provider `default_tags`, plus anything passed in `additional_tags`.
## Requirements
| Name | Version |
|------|---------|
| Terraform | >= 1.6.0 |
| AWS provider | ~> 5.0 |
| Random provider | ~> 3.6 |
AWS credentials must be available in the environment (profile, SSO, or environment variables).
## Usage
```sh
terraform init
terraform plan
terraform apply
```
State is local. Configure a remote backend before using this beyond experiments.
## Inputs
| Name | Type | Default | Description |
|------|------|---------|-------------|
| `project_name` | string | `aether-test` | Resource name prefix; lowercase letters, numbers, hyphens only |
| `environment` | string | `dev` | One of `dev`, `staging`, `prod` |
| `aws_region` | string | `eu-west-2` | AWS region |
| `vpc_cidr` | string | `10.0.0.0/16` | VPC CIDR block |
| `public_subnet_cidrs` | list(string) | `["10.0.1.0/24", "10.0.2.0/24"]` | Public subnet CIDRs |
| `private_subnet_cidrs` | list(string) | `["10.0.10.0/24", "10.0.20.0/24"]` | Private subnet CIDRs |
| `enable_s3_versioning` | bool | `true` | Toggle S3 bucket versioning |
| `create_nat_gateway` | bool | `false` | Create NAT gateway for private subnet egress |
| `additional_tags` | map(string) | `{}` | Extra tags merged into all resources |
## Outputs
VPC ID and CIDR, subnet IDs and CIDRs, internet gateway ID, public and private route table IDs, NAT gateway ID (null when disabled), S3 bucket name/ARN/region, AWS account ID and region, and a `resource_summary` object.
## Cost notes
Everything here is free or near-free except the NAT gateway: enabling `create_nat_gateway` costs roughly $0.05/hour plus data processing charges in eu-west-2. The single-NAT layout is a deliberate simplification for dev use; production would run one NAT per availability zone.
+217
View File
@@ -0,0 +1,217 @@
########################################
# Terraform Configuration
########################################
terraform {
required_version = ">= 1.6.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
random = {
source = "hashicorp/random"
version = "~> 3.6"
}
}
}
########################################
# Provider Configuration
########################################
provider "aws" {
region = var.aws_region
default_tags {
tags = merge(
{
Project = var.project_name
Environment = var.environment
ManagedBy = "Terraform"
CreatedBy = "Aether"
},
var.additional_tags
)
}
}
########################################
# VPC
########################################
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_support = true
enable_dns_hostnames = true
tags = {
Name = "${var.project_name}-${var.environment}-vpc"
}
}
########################################
# Public Subnets
########################################
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
map_public_ip_on_launch = true
tags = {
Name = "${var.project_name}-${var.environment}-public-subnet-${count.index + 1}"
Type = "public"
}
}
########################################
# Private Subnets
########################################
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = data.aws_availability_zones.available.names[count.index]
tags = {
Name = "${var.project_name}-${var.environment}-private-subnet-${count.index + 1}"
Type = "private"
}
}
########################################
# Internet Gateway
########################################
resource "aws_internet_gateway" "main" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-${var.environment}-igw"
}
}
########################################
# Public Route Table
########################################
resource "aws_route_table" "public" {
vpc_id = aws_vpc.main.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.main.id
}
tags = {
Name = "${var.project_name}-${var.environment}-public-rt"
}
}
resource "aws_route_table_association" "public" {
count = length(aws_subnet.public)
subnet_id = aws_subnet.public[count.index].id
route_table_id = aws_route_table.public.id
}
########################################
# NAT Gateway (optional)
########################################
resource "aws_eip" "nat" {
count = var.create_nat_gateway ? 1 : 0
domain = "vpc"
tags = {
Name = "${var.project_name}-${var.environment}-nat-eip"
}
depends_on = [aws_internet_gateway.main]
}
resource "aws_nat_gateway" "main" {
count = var.create_nat_gateway ? 1 : 0
allocation_id = aws_eip.nat[0].id
subnet_id = aws_subnet.public[0].id
tags = {
Name = "${var.project_name}-${var.environment}-nat"
}
depends_on = [aws_internet_gateway.main]
}
########################################
# Private Route Table
########################################
resource "aws_route_table" "private" {
vpc_id = aws_vpc.main.id
tags = {
Name = "${var.project_name}-${var.environment}-private-rt"
}
}
resource "aws_route" "private_nat" {
count = var.create_nat_gateway ? 1 : 0
route_table_id = aws_route_table.private.id
destination_cidr_block = "0.0.0.0/0"
nat_gateway_id = aws_nat_gateway.main[0].id
}
resource "aws_route_table_association" "private" {
count = length(aws_subnet.private)
subnet_id = aws_subnet.private[count.index].id
route_table_id = aws_route_table.private.id
}
########################################
# S3 Bucket for Application Data
########################################
resource "aws_s3_bucket" "app_data" {
bucket = "${var.project_name}-${var.environment}-app-data-${random_id.bucket_suffix.hex}"
tags = {
Name = "${var.project_name}-${var.environment}-app-data"
Description = "Application data storage"
}
}
resource "aws_s3_bucket_versioning" "app_data" {
bucket = aws_s3_bucket.app_data.id
versioning_configuration {
status = var.enable_s3_versioning ? "Enabled" : "Disabled"
}
}
resource "aws_s3_bucket_public_access_block" "app_data" {
bucket = aws_s3_bucket.app_data.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
########################################
# Random ID for unique bucket naming
########################################
resource "random_id" "bucket_suffix" {
byte_length = 4
}
########################################
# Data Sources
########################################
data "aws_availability_zones" "available" {
state = "available"
}
data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
+105
View File
@@ -0,0 +1,105 @@
########################################
# VPC Outputs
########################################
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "vpc_cidr" {
description = "CIDR block of the VPC"
value = aws_vpc.main.cidr_block
}
########################################
# Subnet Outputs
########################################
output "public_subnet_ids" {
description = "IDs of public subnets"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "IDs of private subnets"
value = aws_subnet.private[*].id
}
output "public_subnet_cidrs" {
description = "CIDR blocks of public subnets"
value = aws_subnet.public[*].cidr_block
}
output "private_subnet_cidrs" {
description = "CIDR blocks of private subnets"
value = aws_subnet.private[*].cidr_block
}
########################################
# Networking Outputs
########################################
output "internet_gateway_id" {
description = "ID of the Internet Gateway"
value = aws_internet_gateway.main.id
}
output "public_route_table_id" {
description = "ID of the public route table"
value = aws_route_table.public.id
}
output "private_route_table_id" {
description = "ID of the private route table"
value = aws_route_table.private.id
}
output "nat_gateway_id" {
description = "ID of the NAT Gateway (null if not created)"
value = var.create_nat_gateway ? aws_nat_gateway.main[0].id : null
}
########################################
# S3 Outputs
########################################
output "s3_bucket_name" {
description = "Name of the S3 bucket"
value = aws_s3_bucket.app_data.id
}
output "s3_bucket_arn" {
description = "ARN of the S3 bucket"
value = aws_s3_bucket.app_data.arn
}
output "s3_bucket_region" {
description = "Region of the S3 bucket"
value = aws_s3_bucket.app_data.region
}
########################################
# AWS Account Information
########################################
output "aws_account_id" {
description = "AWS Account ID"
value = data.aws_caller_identity.current.account_id
}
output "aws_region" {
description = "AWS Region"
value = data.aws_region.current.name
}
########################################
# Resource Summary
########################################
output "resource_summary" {
description = "Summary of created resources"
value = {
vpc_created = true
public_subnets_count = length(aws_subnet.public)
private_subnets_count = length(aws_subnet.private)
s3_bucket_created = true
s3_versioning_enabled = var.enable_s3_versioning
internet_gateway_created = true
nat_gateway_created = var.create_nat_gateway
}
}
+83
View File
@@ -0,0 +1,83 @@
########################################
# Project Configuration
########################################
variable "project_name" {
type = string
description = "Name of the project (used for resource naming)"
default = "aether-test"
validation {
condition = can(regex("^[a-z0-9-]+$", var.project_name))
error_message = "Project name must contain only lowercase letters, numbers, and hyphens."
}
}
variable "environment" {
type = string
description = "Environment name (dev, staging, prod)"
default = "dev"
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
########################################
# AWS Configuration
########################################
variable "aws_region" {
type = string
description = "AWS region for resources"
default = "eu-west-2"
}
########################################
# Network Configuration
########################################
variable "vpc_cidr" {
type = string
description = "CIDR block for the VPC"
default = "10.0.0.0/16"
validation {
condition = can(cidrhost(var.vpc_cidr, 0))
error_message = "Must be a valid IPv4 CIDR block."
}
}
variable "public_subnet_cidrs" {
type = list(string)
description = "CIDR blocks for public subnets"
default = ["10.0.1.0/24", "10.0.2.0/24"]
}
variable "private_subnet_cidrs" {
type = list(string)
description = "CIDR blocks for private subnets"
default = ["10.0.10.0/24", "10.0.20.0/24"]
}
########################################
# Feature Flags
########################################
variable "enable_s3_versioning" {
type = bool
description = "Enable versioning for S3 buckets"
default = true
}
variable "create_nat_gateway" {
type = bool
description = "Create NAT Gateway for private subnet internet access"
default = false
}
########################################
# Tags
########################################
variable "additional_tags" {
type = map(string)
description = "Additional tags to apply to all resources"
default = {}
}