diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..063723d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "cSpell.words": [ + "jreavesbucket", + "mysqldb", + "remotestate" + ] +} \ No newline at end of file diff --git "a/C\357\200\272Usersj_c_rAppDataLocalTempvscode-tf-commands_outterraform-validate-2025-05-23T23-18-40.955Z.validate.txt" "b/C\357\200\272Usersj_c_rAppDataLocalTempvscode-tf-commands_outterraform-validate-2025-05-23T23-18-40.955Z.validate.txt" new file mode 100644 index 0000000..e83eb0e --- /dev/null +++ "b/C\357\200\272Usersj_c_rAppDataLocalTempvscode-tf-commands_outterraform-validate-2025-05-23T23-18-40.955Z.validate.txt" @@ -0,0 +1,2 @@ +Success! The configuration is valid. + diff --git a/backend.tf b/backend.tf new file mode 100644 index 0000000..6802b6d --- /dev/null +++ b/backend.tf @@ -0,0 +1,9 @@ +# terraform { +# backend "s3" { +# bucket = "remotestate-jreavesbucket-dev" +# key = "global/s3/terraform.tfstate" +# region = "us-east-1" +# dynamodb_table = "terraform-locks" # <- This enables locking +# encrypt = true +# } +# } \ No newline at end of file diff --git a/cicd_bootstrap/providers.tf b/cicd_bootstrap/providers.tf new file mode 100644 index 0000000..e70fb2f --- /dev/null +++ b/cicd_bootstrap/providers.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = "us-east-1" +} \ No newline at end of file diff --git a/main.tf b/main.tf index 00d0c67..9bd6253 100644 --- a/main.tf +++ b/main.tf @@ -1,125 +1,127 @@ -### PROVIDER -provider "google" { - project = var.project-id - region = var.region - zone = var.zone -} +terraform { + required_version = "~> 1.11.4" -### NETWORK -data "google_compute_network" "default" { - name = "default" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.96.0" + } + } } -## SUBNET -resource "google_compute_subnetwork" "subnet-1" { - name = var.subnet-name - ip_cidr_range = var.subnet-cidr - network = data.google_compute_network.default.self_link - region = var.region - private_ip_google_access = var.private_google_access +provider "aws" { + region = var.region } -resource "google_compute_firewall" "default" { - name = "test-firewall" - network = data.google_compute_network.default.self_link +data "aws_ami" "amazon_linux" { + most_recent = true + owners = [var.ami_owner] - allow { - protocol = "icmp" + filter { + name = "name" + values = [var.ami_name] } +} - allow { - protocol = "tcp" - ports = var.firewall-ports +resource "aws_instance" "nginx_proxy" { + ami = data.aws_ami.amazon_linux.id + instance_type = var.instance_type + subnet_id = aws_subnet.subnet-1.id + vpc_security_group_ids = [aws_security_group.web_sg.id] + associate_public_ip_address = true + + tags = { + Name = "nginx-proxy" } - source_tags = var.compute-source-tags + user_data = <<-EOF + #!/bin/bash + sudo yum install -y nginx + sudo systemctl enable nginx + sudo systemctl start nginx + EOF } -### COMPUTE -## NGINX PROXY -resource "google_compute_instance" "nginx_instance" { - name = "nginx-proxy" - machine_type = "f1-micro" - tags = var.compute-source-tags +resource "aws_instance" "web-instances" { + count = 3 + ami = data.aws_ami.amazon_linux.id + instance_type = var.instance_type + subnet_id = aws_subnet.subnet-1.id + vpc_security_group_ids = [aws_security_group.web_sg.id] - boot_disk { - initialize_params { - image = "debian-cloud/debian-11" - } + tags = { + Name = "web-instance-${count.index + 1}" + Environment = var.target_environment } - network_interface { - network = data.google_compute_network.default.self_link - subnetwork = google_compute_subnetwork.subnet-1.self_link - access_config { - - } - } + user_data = <<-EOF + #!/bin/bash + sudo yum install -y httpd + sudo systemctl enable httpd + sudo systemctl start httpd + EOF } -## WEB1 -resource "google_compute_instance" "web1" { - name = "web1" - machine_type = "f1-micro" - - boot_disk { - initialize_params { - image = "debian-cloud/debian-11" - } - } +resource "aws_instance" "web-map-instances" { + for_each = var.environment_instance_settings - network_interface { - # A default network is created for all GCP projects - network = data.google_compute_network.default.self_link - subnetwork = google_compute_subnetwork.subnet-1.self_link - } -} -## WEB2 -resource "google_compute_instance" "web2" { - name = "web2" - machine_type = "f1-micro" - - boot_disk { - initialize_params { - image = "debian-cloud/debian-11" - } - } + ami = data.aws_ami.amazon_linux.id + instance_type = each.value.instance_type + subnet_id = aws_subnet.subnet-1.id + vpc_security_group_ids = [aws_security_group.web_sg.id] - network_interface { - network = data.google_compute_network.default.self_link - subnetwork = google_compute_subnetwork.subnet-1.self_link - } -} -## WEB3 -resource "google_compute_instance" "web3" { - name = "web3" - machine_type = "f1-micro" - - boot_disk { - initialize_params { - image = "debian-cloud/debian-11" - } - } + tags = each.value.tags - network_interface { - network = data.google_compute_network.default.self_link - subnetwork = google_compute_subnetwork.subnet-1.self_link - } + user_data = <<-EOF + #!/bin/bash + sudo yum install -y httpd + sudo systemctl enable httpd + sudo systemctl start httpd + EOF } -## DB -resource "google_compute_instance" "mysqldb" { - name = "mysqldb" - machine_type = "f1-micro" - - boot_disk { - initialize_params { - image = "debian-cloud/debian-11" - } - } +# resource "aws_dynamodb_table" "mysqldb" { +# name = "mysqldb" +# billing_mode = "PAY_PER_REQUEST" +# hash_key = "id" + +# attribute { +# name = "id" +# type = "S" +# } + +# tags = { +# Name = "mysqldb" +# } +# } + +# module "remote_state_bucket" { +# source = "terraform-aws-modules/s3-bucket/aws" +# version = "~> 4.0" + +# bucket = "remotestate-jreavesbucket-dev" +# acl = "private" + +# control_object_ownership = true +# object_ownership = "ObjectWriter" + +# versioning = { +# enabled = true +# } + +# tags = { +# Environment = "dev" +# Purpose = "Terraform Remote State" +# } +# } + +# resource "aws_dynamodb_table" "terraform_locks" { +# name = "terraform-locks" +# billing_mode = "PAY_PER_REQUEST" +# hash_key = "LockID" - network_interface { - network = data.google_compute_network.default.self_link - subnetwork = google_compute_subnetwork.subnet-1.self_link - } -} \ No newline at end of file +# attribute { +# name = "LockID" +# type = "S" +# } +#} \ No newline at end of file diff --git a/modules/iam_environment_roles/main.tf b/modules/iam_environment_roles/main.tf new file mode 100644 index 0000000..c65085f --- /dev/null +++ b/modules/iam_environment_roles/main.tf @@ -0,0 +1,46 @@ +resource "aws_iam_role" "iam-role" { + name = "terraform-${var.env}-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "" + Principal = { + AWS = "arn:aws:iam::${var.aws_account_id}:root" + } + } + ] + }) + + tags = { + tag-key = "tag-${var.env}-role" + } +} + +resource "aws_iam_role_policy_attachment" "role-policy" { + role = aws_iam_role.iam-role.name + policy_arn = "arn:aws:iam::aws:policy/AdministratorAccess" +} + +module "dev_role" { + source = "./modules/iam_environment_roles" + env = "dev" +} + +module "staging_role" { + source = "./modules/iam_environment_roles" + env = "staging" +} + +module "prod_role" { + source = "./modules/iam_environment_roles" + env = "prod" +} + +module "test_role" { + source = "./modules/iam_environment_roles" + env = "test" +} \ No newline at end of file diff --git a/modules/iam_environment_roles/outputs.tf b/modules/iam_environment_roles/outputs.tf new file mode 100644 index 0000000..04c2475 --- /dev/null +++ b/modules/iam_environment_roles/outputs.tf @@ -0,0 +1,3 @@ +output "role_arn" { + value = aws_iam_role.iam-role.arn +} \ No newline at end of file diff --git a/modules/iam_environment_roles/variables.tf b/modules/iam_environment_roles/variables.tf new file mode 100644 index 0000000..f4957bf --- /dev/null +++ b/modules/iam_environment_roles/variables.tf @@ -0,0 +1,10 @@ +variable "env" { + description = "The environment name" + type = string +} + +variable "aws_account_id" { + description = "The AWS account ID" + type = string + default = "986559698266" +} \ No newline at end of file diff --git a/networking.tf b/networking.tf new file mode 100644 index 0000000..5f51d53 --- /dev/null +++ b/networking.tf @@ -0,0 +1,71 @@ +resource "aws_vpc" "main" { + cidr_block = var.vpc_cidr + enable_dns_hostnames = true + tags = { + Name = "main-vpc" + } +} + +# SUBNET +resource "aws_subnet" "subnet-1" { + vpc_id = aws_vpc.main.id + cidr_block = var.subnet_cidr + map_public_ip_on_launch = true + tags = { + Name = var.subnet_name + } +} + +resource "aws_internet_gateway" "igw" { + vpc_id = aws_vpc.main.id + + tags = { + Name = "main-igw" + } +} + +resource "aws_route_table" "public_rt" { + vpc_id = aws_vpc.main.id + + route { + cidr_block = var.sg_cidr + gateway_id = aws_internet_gateway.igw.id + } + + tags = { + Name = "public-rt" + } +} + +resource "aws_route_table_association" "a" { + subnet_id = aws_subnet.subnet-1.id + route_table_id = aws_route_table.public_rt.id +} + +resource "aws_security_group" "web_sg" { + name = "main-web-sg" + description = "Allow SSH, HTTP, HTTPS" + vpc_id = aws_vpc.main.id + + dynamic "ingress" { + for_each = var.allowed_ports + content { + from_port = ingress.value + to_port = ingress.value + protocol = "tcp" + cidr_blocks = [var.sg_cidr] + } + } + + egress { + description = "Allow all outbound" + from_port = 0 + to_port = 0 + protocol = "-1" + cidr_blocks = [var.sg_cidr] + } + + tags = { + Name = "web-sg" + } +} \ No newline at end of file diff --git a/outputs.tf b/outputs.tf new file mode 100644 index 0000000..41063df --- /dev/null +++ b/outputs.tf @@ -0,0 +1,34 @@ +output "vpc_id" { + description = "VPC ID" + value = aws_vpc.main.id +} + +output "public_subnet_ids" { + description = "Public Subnet IDs" + value = aws_subnet.subnet-1.id +} + +output "web_instance_ips" { + description = "Web Instance IPs" + value = [for instance in aws_instance.web-instances : instance.private_ip] +} + +output "internet_gateway" { + description = "Internet Gateway ID" + value = aws_internet_gateway.igw.id +} + +output "route_table" { + description = "Route Table ID" + value = aws_route_table.public_rt.id +} + +output "nginx_id" { + description = "Nginx Instance ID" + value = aws_instance.nginx_proxy.id +} + +output "nginx_public_ip" { + description = "Nginx Public IPcl" + value = aws_instance.nginx_proxy.public_ip +} \ No newline at end of file diff --git a/override.tf b/override.tf new file mode 100644 index 0000000..abe357a --- /dev/null +++ b/override.tf @@ -0,0 +1,3 @@ +terraform { + backend "local" {} +} \ No newline at end of file diff --git a/remotestate/main.tf b/remotestate/main.tf new file mode 100644 index 0000000..67bc900 --- /dev/null +++ b/remotestate/main.tf @@ -0,0 +1,10 @@ +terraform { + required_version = "~> 1.11.4, < 2.0.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.96.0" + } + } +} \ No newline at end of file diff --git a/serviceaccounts.tf b/serviceaccounts.tf new file mode 100644 index 0000000..b1e7cf4 --- /dev/null +++ b/serviceaccounts.tf @@ -0,0 +1,21 @@ +module "iam_assumable_roles" { + source = "terraform-aws-modules/iam/aws//modules/iam-assumable-role" + version = "~> 5.0" + + for_each = var.environment_instance_settings + + trusted_role_arns = [ + "arn:aws:iam::986559698266:root", + "arn:aws:iam::986559698266:user/jreaves" + ] + + create_role = true + + role_name = "${each.key}-role" + role_requires_mfa = true + + custom_role_policy_arns = [ + "arn:aws:iam::aws:policy/AmazonEC2FullAccess", + "arn:aws:iam::aws:policy/AmazonS3FullAccess" + ] +} \ No newline at end of file diff --git a/stroage.tf b/stroage.tf new file mode 100644 index 0000000..eb98be2 --- /dev/null +++ b/stroage.tf @@ -0,0 +1,9 @@ +resource "aws_s3_bucket" "environment_buckets" { + for_each = toset(var.environment_list) + bucket = "${lower(each.key)}-jreaves-environment" + + tags = { + Name = "environment-${each.key}" + Environment = upper(each.key) + } +} \ No newline at end of file diff --git a/variables.tf b/variables.tf index 2ca35f9..2040a7f 100644 --- a/variables.tf +++ b/variables.tf @@ -1,90 +1,102 @@ -### VARIABLES -variable "project-id" { - type = string +variable "region" { + type = string + default = "us-east-1" } -variable "region" { - type = string - default = "us-central1" +variable "vpc_cidr" { + type = string + default = "10.0.0.0/16" +} + +variable "subnet_name" { + type = string + default = "public-subnet-1" } -variable "zone" { - type = string - default = "us-central1-a" +variable "subnet_cidr" { + type = string + default = "10.0.1.0/24" } -variable "subnet-name" { - type = string - default = "subnet1" +variable "ami_owner" { + type = string + default = "amazon" } -variable "subnet-cidr" { - type = string - default = "10.127.0.0/20" +variable "ami_name" { + type = string + default = "amzn2-ami-hvm-*-x86_64-gp2" } -variable "private_google_access" { - type = bool - default = true +variable "instance_type" { + type = string + default = "t2.micro" } -variable "firewall-ports" { - type = list - default = ["80", "8080", "1000-2000", "22"] +variable "allowed_ports" { + type = list(number) + default = [22, 80, 443] } -variable "compute-source-tags" { - type = list - default = ["web"] +variable "sg_cidr" { + type = string + default = "0.0.0.0/0" } variable "target_environment" { - default = "DEV" + description = "The target environment to deploy into" + type = string + default = "DEV" } variable "environment_list" { - type = list(string) - default = ["DEV","QA","STAGE","PROD"] + type = list(string) + default = ["DEV", "QA", "STAGE", "PROD"] } variable "environment_map" { type = map(string) default = { - "DEV" = "dev", - "QA" = "qa", + "DEV" = "dev", + "QA" = "qa", "STAGE" = "stage", - "PROD" = "prod" - } -} - -variable "environment_machine_type" { - type = map(string) - default = { - "DEV" = "f1-micro", - "QA" = "f1-micro", - "STAGE" = "f1-micro", - "PROD" = "f1-micro" + "PROD" = "prod" } } variable "environment_instance_settings" { - type = map(object({machine_type=string, tags=list(string)})) + type = map(object({ + instance_type = string + tags = map(string) + })) default = { "DEV" = { - machine_type = "f1-micro" - tags = ["dev"] - }, - "QA" = { - machine_type = "f1-micro" - tags = ["qa"] - }, + instance_type = "t2.micro", + tags = { + Name = "dev-instance", + Environment = "DEV" + } + } + "QA" = { + instance_type = "t2.micro", + tags = { + Name = "qa-instance", + Environment = "QA" + } + } "STAGE" = { - machine_type = "f1-micro" - tags = ["stage"] - }, + instance_type = "t2.micro", + tags = { + Name = "stage-instance", + Environment = "STAGE" + } + } "PROD" = { - machine_type = "f1-micro" - tags = ["prod"] + instance_type = "t2.micro", + tags = { + Name = "prod-instance", + Environment = "PROD" + } } } -} \ No newline at end of file +}