diff --git a/README.md b/README.md index 45e1d6c..72239d5 100644 --- a/README.md +++ b/README.md @@ -66,14 +66,14 @@ Once you have chosen the infrastructure module you want to use, move to the modu ## Live Infrastructure -The `live` directory houses our live infrastructure. This is where you'll find our Terraform variables, backend configuration, and Terraform root modules. +The `live` directory houses our live infrastructure components. This is where you'll find our Terraform variables, backend configuration, and Terraform root modules. -It is recommended to create a separate directory for each environment (e.g., `dev`, `staging`, `prod`) and region (e.g., `us-east-1`, `us-west-2`, `eu-west-1`). This allows you to easily manage and deploy your infrastructure. +It is recommended to create a separate directory for each domain that you want to manage with Terraform. For example, you could have a `core-networking` directory for managing your VPC, subnets, and security groups, and a `common-infra` directory for managing your RDS instances, S3 buckets, and other shared resources. -| Module | Description | -| :------------------------------------------------------------------------------- | :---------------------------------------------------- | -| [Prod App Infrastructure (us-west-2)](./live/prod/us-west-2/app/README.md) | Terraform root module for our prod infrastructure. | -| [Staging App Infrastructure (us-west-2)](./live/staging/us-west-2/app/README.md) | Terraform root module for our staging infrastructure. | +| Module | Description | +| :-------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | +| [Common Infrastructure](./live/common-infra/README.md) | Terraform module for managing common infrastructure components such as VPC, subnets, security groups, and RDS instances. | +| [Terraform Backend Configuration](./live/terraform-backend/README.md) | Terraform module for setting up the S3 backend. | ## Terraform Modules diff --git a/live/prod/us-west-2/app/.terraform.lock.hcl b/live/common-infra/.terraform.lock.hcl similarity index 100% rename from live/prod/us-west-2/app/.terraform.lock.hcl rename to live/common-infra/.terraform.lock.hcl diff --git a/live/prod/us-west-2/app/README.md b/live/common-infra/README.md similarity index 60% rename from live/prod/us-west-2/app/README.md rename to live/common-infra/README.md index ac27a78..9c3f3df 100644 --- a/live/prod/us-west-2/app/README.md +++ b/live/common-infra/README.md @@ -1,75 +1,92 @@ -# Prod Infrastructure (us-west-2) +# Common Infrastructure -🏢 This directory contains the infrastructure as code for our cloud infrastructure. It provides a ready-to-use Terraform module with various features. Follow the steps below to get started. +🏢 This directory contains the Terraform configuration for our core cloud infrastructure. It provides a ready-to-use Terraform module with essential services and security features. ## Features -- ✨ Ready to use Root Terraform module! -- 🗄️ Store Terraform state in an S3 bucket with a DynamoDB table for locking. -- 🌐 VPC with public and private subnets (application and database subnets) in three availability zones. -- 🔒 Security groups for bastion host and database. -- 🔑 Bastion host to access private resources. -- 🐘 RDS Postgres instance and other database resources. -- 🔒 AWS Secrets Manager to store database credentials. -- 🔧 SSM Parameter Store to store parameters such as VPC ID, Subnet IDs, etc. +- ✨ Comprehensive Root Terraform module for quick deployment. +- 🗄️ Configured to use an external S3 bucket for Terraform state management with a DynamoDB table for state locking. +- 🌐 Highly available VPC setup with public and private subnets across multiple availability zones. +- 🔒 Configured security groups for bastion hosts and databases. +- 🔑 Bastion host setup for secure access to internal services. +- 🐘 RDS Postgres setup for reliable database services. +- 🔒 Utilization of AWS Secrets Manager for secure storage of database credentials. +- 🔧 Use of SSM Parameter Store for managing network and service parameters. ## Prerequisites -- [Terraform](https://www.terraform.io/downloads.html) -- [TFswitch](https://tfswitch.warrensbox.com/) +- [Terraform](https://www.terraform.io/downloads.html) for infrastructure provisioning. +- [TFswitch](https://tfswitch.warrensbox.com/) to switch between Terraform versions easily. ## Setup -1. Switch to the correct Terraform version +1. **Set Terraform Version:** -```sh -tfswitch -``` + Ensure you are using the correct Terraform version: -2. Initialize the Terraform working directory: + ```sh + tfswitch + ``` -```sh -terraform init -``` +2. **Check the Terraform Backend Configuration:** -## Deploy + Verify that the backend configuration is set correctly in the `backend.tf` file. -1. Plan the deployment: + ```hcl + terraform { + required_version = ">= 1.0.0" -```sh -terraform plan -out ./prod.tfplan -``` + backend "s3" { + region = "us-west-2" + bucket = "terraform-state" + key = "terraform.tfstate" + dynamodb_table = "terraform-state-lock" + profile = "" + role_arn = "" + encrypt = "true" + } + } -2. Apply the deployment: + ``` -```sh -terraform apply ./prod.tfplan -``` + Replace the placeholder values with the actual bucket name, key, region, and DynamoDB table name. -### First Time Deployment? +3. **Initialize Terraform:** -If this is the first time you are deploying, a file called `s3-backend.tf` will be created. This file configures the backend for Terraform, using S3 to store the state of our infrastructure. + Initialize the working directory with the required providers and modules: -Run the following command to copy the state to the S3 bucket: + ```sh + terraform init + ``` -```sh -terraform init -force-copy -``` +4. **Workspace Management:** -Push the `s3-backend.tf` file to the repository: + Select or create a new workspace tailored to your deployment environment: -```sh -git add s3-backend.tf && git commit -m "Add s3-backend.tf file" -git push -``` + ```sh + # Switch to the another workspace or create it if it doesn't exist + terraform workspace select -or-create prod + ``` -## Destroy +## Deploy -To destroy the infrastructure, run the following command: +🚀 **Deployment Instructions:** -```sh -terraform destroy -``` +1. **Plan Your Deployment:** + + Review and verify the deployment plan: + + ```sh + terraform plan -var-file ./configs/prod.tfvars -out=prod.tfplan + ``` + +2. **Execute the Plan:** + + Apply the planned configuration to provision the infrastructure: + + ```sh + terraform apply "prod.tfplan" + ``` ## Post Deployment Steps @@ -193,6 +210,16 @@ You can now execute SQL commands to test the database setup. For example: These steps will help you verify the successful setup of the database and ensure that the necessary connections and configurations are in place. +## Destroy + +💣 **NOTE:** In this example, we are using the `prod` environment and the `us-west-2` region. Modify these values according to your environment and region. + +To destroy the infrastructure, run the following command: + +```sh +terraform destroy -var-file ./configs/prod.tfvars +``` + ## Module Documentation The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running the following command from the module directory: diff --git a/live/prod/us-west-2/app/backend.tf b/live/common-infra/backend.tf similarity index 61% rename from live/prod/us-west-2/app/backend.tf rename to live/common-infra/backend.tf index 0cff6d7..690d171 100644 --- a/live/prod/us-west-2/app/backend.tf +++ b/live/common-infra/backend.tf @@ -6,14 +6,16 @@ module "terraform_state_backend" { source = "cloudposse/tfstate-backend/aws" version = "1.1.1" - name = module.label.name - namespace = module.label.namespace - environment = module.label.environment - attributes = ["state"] + # Avoid creating anything within if we are not in the workspace "default" and "prod". + enabled = contains(["default", "prod"], terraform.workspace) + + name = module.label.name + namespace = module.label.namespace + attributes = ["state"] terraform_backend_config_file_path = "." terraform_backend_config_file_name = "s3-backend.tf" - terraform_state_file = "${module.label.id}.tfstate" + terraform_state_file = "${module.label.namespace}-${module.label.name}.tfstate" bucket_enabled = true dynamodb_enabled = true diff --git a/live/prod/us-west-2/app/bastion.tf b/live/common-infra/bastion.tf similarity index 50% rename from live/prod/us-west-2/app/bastion.tf rename to live/common-infra/bastion.tf index 2b7ffea..452e18e 100644 --- a/live/prod/us-west-2/app/bastion.tf +++ b/live/common-infra/bastion.tf @@ -1,30 +1,30 @@ -locals { - bastion = { - enable = true - } +variable "enable_bastion" { + type = bool + description = "Enable bastion host" + default = false } module "bastion" { - count = local.bastion.enable ? 1 : 0 + count = var.enable_bastion ? 1 : 0 - source = "../../../../modules/bastion" + source = "../../modules/bastion" name = "${module.label.id}-bastion" vpc_id = module.vpc.vpc_id subnets = module.vpc.public_subnets associate_public_ip_address = true - associate_elastic_ip_address = true + associate_elastic_ip_address = false tags = module.label.tags } output "bastion_instance_id" { - value = local.bastion.enable ? module.bastion[0].instance_id : null + value = var.enable_bastion ? module.bastion[0].instance_id : null } output "bastion_instance_profile" { - value = local.bastion.enable ? module.bastion[0].instance_profile : null + value = var.enable_bastion ? module.bastion[0].instance_profile : null } output "ssm_parameter_bastion_ssh_key" { description = "name of the ssm parameter for the bastion ssh key" - value = local.bastion.enable ? module.bastion[0].ssm_parameter_ssh_key : null + value = var.enable_bastion ? module.bastion[0].ssm_parameter_ssh_key : null } diff --git a/live/common-infra/configs/prod.tfvars b/live/common-infra/configs/prod.tfvars new file mode 100644 index 0000000..cc4017d --- /dev/null +++ b/live/common-infra/configs/prod.tfvars @@ -0,0 +1,20 @@ +# General settings + +region = "us-west-2" +name = "core" +namespace = "nan" +environment = "prod" +tags = { + "Terraform" = "true" + "Environment" = "prod" +} + +# AWS settings + +vpc_cidr_block = "10.0.0.0/16" +enable_bastion = false + +# RDS Database settings + +example_db_name = "example" +example_db_master_username = "root" diff --git a/live/common-infra/configs/staging.tfvars b/live/common-infra/configs/staging.tfvars new file mode 100644 index 0000000..cc4017d --- /dev/null +++ b/live/common-infra/configs/staging.tfvars @@ -0,0 +1,20 @@ +# General settings + +region = "us-west-2" +name = "core" +namespace = "nan" +environment = "prod" +tags = { + "Terraform" = "true" + "Environment" = "prod" +} + +# AWS settings + +vpc_cidr_block = "10.0.0.0/16" +enable_bastion = false + +# RDS Database settings + +example_db_name = "example" +example_db_master_username = "root" diff --git a/live/common-infra/context.tf b/live/common-infra/context.tf new file mode 100644 index 0000000..2be61f9 --- /dev/null +++ b/live/common-infra/context.tf @@ -0,0 +1,54 @@ +variable "name" { + description = "Name to use for servers, tags, etc" + type = string + default = "name" +} + +variable "namespace" { + description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" + type = string + default = "development" +} + +variable "environment" { + description = "Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT'" + type = string + default = "development" +} + +variable "stage" { + description = "Stage, e.g. 'build', 'test', 'deploy', 'release'" + type = string + # not required, so no default + default = null +} + +variable "tags" { + description = "Any extra tags to assign to objects" + type = map(any) + default = {} +} + +data "aws_caller_identity" "aws" {} + +locals { + tf_tags = { + Terraform = true, + By = data.aws_caller_identity.aws.arn + } +} + +// Keep labels, tags consistent +module "label" { + source = "cloudposse/label/null" + version = "0.25.0" + + name = var.name + environment = var.environment + namespace = var.namespace + stage = var.stage + + delimiter = "-" + label_order = ["namespace", "environment", "stage", "name", "attributes"] + tags = merge(var.tags, local.tf_tags) +} diff --git a/live/prod/us-west-2/app/docs/MODULE.md b/live/common-infra/docs/MODULE.md similarity index 66% rename from live/prod/us-west-2/app/docs/MODULE.md rename to live/common-infra/docs/MODULE.md index a5e3ad2..94f63be 100644 --- a/live/prod/us-west-2/app/docs/MODULE.md +++ b/live/common-infra/docs/MODULE.md @@ -16,11 +16,11 @@ | Name | Source | Version | |------|--------|---------| -| [bastion](#module\_bastion) | ../../../../modules/bastion | n/a | -| [exampledb](#module\_exampledb) | ../../../../modules/rds | n/a | +| [bastion](#module\_bastion) | ../../modules/bastion | n/a | +| [exampledb](#module\_exampledb) | ../../modules/rds | n/a | | [label](#module\_label) | cloudposse/label/null | 0.25.0 | | [terraform\_state\_backend](#module\_terraform\_state\_backend) | cloudposse/tfstate-backend/aws | 1.1.1 | -| [vpc](#module\_vpc) | ../../../../modules/vpc | n/a | +| [vpc](#module\_vpc) | ../../modules/vpc | n/a | ## Resources @@ -30,7 +30,18 @@ ## Inputs -No inputs. +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [enable\_bastion](#input\_enable\_bastion) | Enable bastion host | `bool` | `false` | no | +| [environment](#input\_environment) | Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT' | `string` | `"development"` | no | +| [example\_db\_master\_username](#input\_example\_db\_master\_username) | The username for the master DB user | `string` | `"root"` | no | +| [example\_db\_name](#input\_example\_db\_name) | The name of the database to create | `string` | `"mydb"` | no | +| [name](#input\_name) | Name to use for servers, tags, etc | `string` | `"name"` | no | +| [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `"development"` | no | +| [region](#input\_region) | AWS region | `string` | `"us-west-2"` | no | +| [stage](#input\_stage) | Stage, e.g. 'build', 'test', 'deploy', 'release' | `string` | `null` | no | +| [tags](#input\_tags) | Any extra tags to assign to objects | `map(any)` | `{}` | no | +| [vpc\_cidr\_block](#input\_vpc\_cidr\_block) | CIDR block for the VPC | `string` | `"10.0.0.0/16"` | no | ## Outputs diff --git a/live/prod/us-west-2/app/example-rds-instance.tf b/live/common-infra/example-rds-instance.tf similarity index 69% rename from live/prod/us-west-2/app/example-rds-instance.tf rename to live/common-infra/example-rds-instance.tf index 46fdade..7b14a28 100644 --- a/live/prod/us-west-2/app/example-rds-instance.tf +++ b/live/common-infra/example-rds-instance.tf @@ -1,20 +1,25 @@ -locals { - exampledb = { - db_name = "mydb" - db_master_username = "myuser" - } +variable "example_db_name" { + description = "The name of the database to create" + type = string + default = "mydb" +} + +variable "example_db_master_username" { + description = "The username for the master DB user" + type = string + default = "root" } module "exampledb" { - source = "../../../../modules/rds" + source = "../../modules/rds" name = "${module.label.id}-exampledb" vpc_id = module.vpc.vpc_id db_subnet_group = module.vpc.database_subnet_group - db_name = local.exampledb.db_name - db_master_username = local.exampledb.db_master_username + db_name = var.example_db_name + db_master_username = var.example_db_master_username db_port = 5432 allocated_storage = 20 diff --git a/live/prod/us-west-2/app/main.tf b/live/common-infra/main.tf similarity index 78% rename from live/prod/us-west-2/app/main.tf rename to live/common-infra/main.tf index fe5e8dc..143ee86 100644 --- a/live/prod/us-west-2/app/main.tf +++ b/live/common-infra/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "us-west-2" + region = var.region default_tags { tags = { diff --git a/live/prod/us-west-2/app/outputs.tf b/live/common-infra/outputs.tf similarity index 100% rename from live/prod/us-west-2/app/outputs.tf rename to live/common-infra/outputs.tf diff --git a/live/common-infra/variables.tf b/live/common-infra/variables.tf new file mode 100644 index 0000000..098424d --- /dev/null +++ b/live/common-infra/variables.tf @@ -0,0 +1,5 @@ +variable "region" { + description = "AWS region" + type = string + default = "us-west-2" +} diff --git a/live/prod/us-west-2/app/versions.tf b/live/common-infra/versions.tf similarity index 100% rename from live/prod/us-west-2/app/versions.tf rename to live/common-infra/versions.tf diff --git a/live/prod/us-west-2/app/vpc.tf b/live/common-infra/vpc.tf similarity index 84% rename from live/prod/us-west-2/app/vpc.tf rename to live/common-infra/vpc.tf index 16a3f0b..ae45af9 100644 --- a/live/prod/us-west-2/app/vpc.tf +++ b/live/common-infra/vpc.tf @@ -1,7 +1,13 @@ +variable "vpc_cidr_block" { + description = "CIDR block for the VPC" + type = string + default = "10.0.0.0/16" +} + module "vpc" { - source = "../../../../modules/vpc" + source = "../../modules/vpc" name = module.label.id - vpc_cidr_block = "10.0.0.0/16" + vpc_cidr_block = var.vpc_cidr_block tags = module.label.tags enable_nat_gateway = true single_nat_gateway = true diff --git a/live/prod/us-west-2/app/context.tf b/live/prod/us-west-2/app/context.tf deleted file mode 100644 index 29e3340..0000000 --- a/live/prod/us-west-2/app/context.tf +++ /dev/null @@ -1,34 +0,0 @@ -locals { - context = { - name = "app" - namespace = "nan" - environment = "prod" - tags = { - "Terraform" = "true" - "Environment" = "prod" - } - } -} - -data "aws_caller_identity" "aws" {} - -locals { - tf_tags = { - Terraform = true, - By = data.aws_caller_identity.aws.arn - } -} - -// Keep labels, tags consistent -module "label" { - source = "cloudposse/label/null" - version = "0.25.0" - - name = local.context.name - environment = local.context.environment - namespace = local.context.namespace - - delimiter = "-" - label_order = ["namespace", "environment", "name", "attributes"] - tags = merge(local.context.tags, local.tf_tags) -} diff --git a/live/staging/us-west-2/app/README.md b/live/staging/us-west-2/app/README.md deleted file mode 100644 index bf0c60c..0000000 --- a/live/staging/us-west-2/app/README.md +++ /dev/null @@ -1,204 +0,0 @@ -# Staging Infrastructure (us-west-2) - -🏢 This directory contains the infrastructure as code for our cloud infrastructure. It provides a ready-to-use Terraform module with various features. Follow the steps below to get started. - -## Features - -- ✨ Ready to use Root Terraform module! -- 🗄️ Store Terraform state in an S3 bucket with a DynamoDB table for locking. -- 🌐 VPC with public and private subnets (application and database subnets) in three availability zones. -- 🔒 Security groups for bastion host and database. -- 🔑 Bastion host to access private resources. -- 🐘 RDS Postgres instance and other database resources. -- 🔒 AWS Secrets Manager to store database credentials. -- 🔧 SSM Parameter Store to store parameters such as VPC ID, Subnet IDs, etc. - -## Prerequisites - -- [Terraform](https://www.terraform.io/downloads.html) -- [TFswitch](https://tfswitch.warrensbox.com/) - -## Setup - -1. Switch to the correct Terraform version - -```sh -tfswitch -``` - -2. Initialize the Terraform working directory: - -```sh -terraform init -``` - -## Deploy - -1. Plan the deployment: - -```sh -terraform plan -out ./staging.tfplan -``` - -2. Apply the deployment: - -```sh -terraform apply ./staging.tfplan -``` - -### First Time Deployment? - -If this is the first time you are deploying, a file called `s3-backend.tf` will be created. This file configures the backend for Terraform, using S3 to store the state of our infrastructure. - -Run the following command to copy the state to the S3 bucket: - -```sh -terraform init -force-copy -``` - -Push the `s3-backend.tf` file to the repository: - -```sh -git add s3-backend.tf && git commit -m "Add s3-backend.tf file" -git push -``` - -## Destroy - -To destroy the infrastructure, run the following command: - -```sh -terraform destroy -``` - -## Post Deployment Steps - -After successfully deploying the infrastructure, follow these steps to test the deployment and ensure everything is working as expected: - -### Accessing the Parameter Store - -Retrieve stored values, such as the VPC ID, using the AWS Parameter Store: - -```bash -# Retrieve the parameter value from the AWS Parameter Store -vpc_id_parameter_name=$(terraform output -json | jq -r '.ssm_parameter_vpc_id.value') -vpc_id=$(aws ssm get-parameter --name "$vpc_id_parameter_name" --query 'Parameter.Value' --output text) - -# Print the value -echo "VPC ID: $vpc_id" -``` - -### Connecting to the Bastion Host - -To establish a secure connection with the bastion host, follow these steps: - -#### Obtain Required Information - -First, you need to gather some essential information: - -- Bastion SSH Parameter Name -- Bastion Instance ID - -You can retrieve these values using Terraform: - -```bash -bastion_ssh_parameter_name=$(terraform output -json | jq -r '.ssm_parameter_bastion_ssh_key.value') -bastion_instance_id=$(terraform output -json | jq -r '.bastion_instance_id.value') -``` - -#### Generate .pem file with the ssh key - -```bash -aws ssm get-parameter --name "$bastion_ssh_parameter_name" --with-decryption --query 'Parameter.Value' --output text > /tmp/ssh_key.pem -chmod 400 /tmp/ssh_key.pem -``` - -#### Retrieve bastion's public IP - -```bash -bastion_public_ip=$(aws ec2 describe-instances --instance-ids "$bastion_instance_id" --query 'Reservations[0].Instances[0].PublicIpAddress' --output text | tr '.' '-') - -# Print the value -echo "Bastion IP: $bastion_public_ip" -``` - -#### Connect to Bastion Host - -```bash -ssh -i "/tmp/ssh_key.pem" ubuntu@ec2-"$bastion_public_ip".us-west-2.compute.amazonaws.com -``` - -Ensure that you can access the database from the bastion host and verify that Docker is functioning correctly. - -#### Testing Docker and Internet Access - -To verify internet access and Docker functionality, execute the following commands: - -```bash -# Test Internet Access -ping -c 3 google.com - -# Test Docker -docker run -it --rm hello-world -``` - -#### Connecting to the Database - -To connect to the database from the bastion host, retrieve the connection information from AWS Secrets Manager. Follow these steps: - -- Outside the bastion host: - -```bash -# Retrieve the parameter value from the AWS Parameter Store -SECRET_ID=$(terraform output -json | jq -r '.example_db_connection_secret_arn.value') -``` - -- Inside the bastion host: - -```bash -# Retrieve the connection information from AWS Secrets Manager -db_secret=$(aws secretsmanager get-secret-value --secret-id "$SECRET_ID" \ - --query 'SecretString' --output json) - -# Parse the connection information to obtain the username, password, host, port, and database name -db_username=$(echo $db_secret | jq -r '.username') -db_password=$(echo $db_secret | jq -r '.password') -db_host=$(echo $db_secret | jq -r '.host') -db_port=$(echo $db_secret | jq -r '.port') -db_name=$(echo $db_secret | jq -r '.dbname') - -# Connect to the database using Psql with Docker -docker run -it --rm postgres:14.0-alpine psql -h $db_host -p $db_port -U $db_username -d $db_name -``` - -You can now execute SQL commands to test the database setup. For example: - -- Retrieve the current date and time: - -```sql -> SELECT NOW(); -``` - -- Check the database version: - -```sql -> SELECT version(); -``` - -- List the tables in the database: - -```sql -> SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'; -``` - -These steps will help you verify the successful setup of the database and ensure that the necessary connections and configurations are in place. - -## Module Documentation - -The module documentation is generated with [terraform-docs](https://github.com/terraform-docs/terraform-docs) by running the following command from the module directory: - -```sh -terraform-docs md . > ./docs/MODULE.md -``` - -You can also view the latest version of the module documentation [here](./docs/MODULE.md). diff --git a/live/staging/us-west-2/app/bastion.tf b/live/staging/us-west-2/app/bastion.tf deleted file mode 100644 index 2b7ffea..0000000 --- a/live/staging/us-west-2/app/bastion.tf +++ /dev/null @@ -1,30 +0,0 @@ -locals { - bastion = { - enable = true - } -} - -module "bastion" { - count = local.bastion.enable ? 1 : 0 - - source = "../../../../modules/bastion" - name = "${module.label.id}-bastion" - vpc_id = module.vpc.vpc_id - subnets = module.vpc.public_subnets - associate_public_ip_address = true - associate_elastic_ip_address = true - tags = module.label.tags -} - -output "bastion_instance_id" { - value = local.bastion.enable ? module.bastion[0].instance_id : null -} - -output "bastion_instance_profile" { - value = local.bastion.enable ? module.bastion[0].instance_profile : null -} - -output "ssm_parameter_bastion_ssh_key" { - description = "name of the ssm parameter for the bastion ssh key" - value = local.bastion.enable ? module.bastion[0].ssm_parameter_ssh_key : null -} diff --git a/live/staging/us-west-2/app/context.tf b/live/staging/us-west-2/app/context.tf deleted file mode 100644 index 8090493..0000000 --- a/live/staging/us-west-2/app/context.tf +++ /dev/null @@ -1,34 +0,0 @@ -locals { - context = { - name = "app" - namespace = "nan" - environment = "staging" - tags = { - "Terraform" = "true" - "Environment" = "staging" - } - } -} - -data "aws_caller_identity" "aws" {} - -locals { - tf_tags = { - Terraform = true, - By = data.aws_caller_identity.aws.arn - } -} - -// Keep labels, tags consistent -module "label" { - source = "cloudposse/label/null" - version = "0.25.0" - - name = local.context.name - environment = local.context.environment - namespace = local.context.namespace - - delimiter = "-" - label_order = ["namespace", "environment", "name", "attributes"] - tags = merge(local.context.tags, local.tf_tags) -} diff --git a/live/staging/us-west-2/app/example-rds-instance.tf b/live/staging/us-west-2/app/example-rds-instance.tf deleted file mode 100644 index 46fdade..0000000 --- a/live/staging/us-west-2/app/example-rds-instance.tf +++ /dev/null @@ -1,45 +0,0 @@ -locals { - exampledb = { - db_name = "mydb" - db_master_username = "myuser" - } -} - -module "exampledb" { - source = "../../../../modules/rds" - - name = "${module.label.id}-exampledb" - - vpc_id = module.vpc.vpc_id - db_subnet_group = module.vpc.database_subnet_group - - db_name = local.exampledb.db_name - db_master_username = local.exampledb.db_master_username - db_port = 5432 - - allocated_storage = 20 - - manage_master_user_password = true - - tags = module.label.tags -} - -output "example_db_instance_address" { - description = "The address of the RDS instance" - value = module.exampledb.db_instance_address -} - -output "example_db_instance_port" { - description = "The database port" - value = module.exampledb.db_instance_port -} - -output "example_db_instance_name" { - description = "The database name" - value = module.exampledb.db_instance_name -} - -output "example_db_instance_master_user_secret_arn" { - description = "The ARN of the secret containing the connection details for the RDS instance" - value = module.exampledb.db_instance_master_user_secret_arn -} diff --git a/live/staging/us-west-2/app/vpc.tf b/live/staging/us-west-2/app/vpc.tf deleted file mode 100644 index 1da0966..0000000 --- a/live/staging/us-west-2/app/vpc.tf +++ /dev/null @@ -1,38 +0,0 @@ -module "vpc" { - source = "../../../../modules/vpc" - name = module.label.id - vpc_cidr_block = "10.1.0.0/16" - tags = module.label.tags - enable_nat_gateway = true - single_nat_gateway = true -} - -output "ssm_parameter_vpc_id" { - description = "name of the ssm parameter for the vpc id" - value = module.vpc.ssm_parameter_vpc_id -} - -output "ssm_parameter_public_subnets" { - description = "name of the ssm parameter for the public subnets" - value = module.vpc.ssm_parameter_public_subnets -} - -output "ssm_parameter_private_subnets" { - description = "name of the ssm parameter for the private subnets" - value = module.vpc.ssm_parameter_private_subnets -} - -output "ssm_parameter_database_subnets" { - description = "name of the ssm parameter for the database subnets" - value = module.vpc.ssm_parameter_database_subnets -} - -output "ssm_parameter_app_subnets" { - description = "name of the ssm parameter for the app subnets" - value = module.vpc.ssm_parameter_app_subnets -} - -output "ssm_parameter_app_security_group" { - description = "name of the ssm parameter for the app security group" - value = module.vpc.ssm_parameter_app_security_group -} diff --git a/live/staging/us-west-2/app/.terraform.lock.hcl b/live/terraform-backend/.terraform.lock.hcl similarity index 100% rename from live/staging/us-west-2/app/.terraform.lock.hcl rename to live/terraform-backend/.terraform.lock.hcl diff --git a/live/terraform-backend/README.md b/live/terraform-backend/README.md new file mode 100644 index 0000000..6d6cf9a --- /dev/null +++ b/live/terraform-backend/README.md @@ -0,0 +1,77 @@ +# Terraform S3 Backend Configuration + +🔒 This directory contains the Terraform configuration for setting up and managing the S3 backend used for storing the state of our cloud infrastructure securely. + +## Features + +- ✨ Utilization of the [cloudposse/tfstate-backend/aws](https://github.com/cloudposse/terraform-aws-tfstate-backend) module for robust backend setup. +- 🗄️ Secure storage of Terraform state in an S3 bucket. +- 🔒 DynamoDB table for state locking to prevent concurrent state modifications. + +## Prerequisites + +- [Terraform](https://www.terraform.io/downloads.html) installed on your machine. +- Proper AWS credentials configured that can access the necessary resources. +- [TFswitch](https://tfswitch.warrensbox.com/) for managing Terraform versions. + +## Setup + +1. **Initialize Terraform:** + + Initialize the Terraform working directory which will download the necessary providers and modules: + + ```sh + terraform init + ``` + +## Deploy + +1. **Plan the Deployment:** + + Generate an execution plan for Terraform: + + ```sh + terraform plan -var-file ./configs/common-infra.tfvars -out ./common-infra.tfplan + ``` + +2. **Apply the Configuration:** + + Apply the configuration to set up the S3 bucket and DynamoDB table: + + ```sh + terraform apply "./common-infra.tfplan" + ``` + + 🚀 **NOTE:** Confirm the actions before proceeding to ensure that the correct resources are being created or modified. + +### First Time Deployment? + +If this is your first deployment, Terraform will prompt you to confirm the setup of the backend. This is a critical step as it involves creating resources that will handle your Terraform state. + +- **Initiate Backend Transfer:** + + If migrating from a local state, use the following command to migrate the state to the S3 bucket safely: + + ```sh + terraform init -force-copy + ``` + + Push the changes to your version control system: + + ```sh + git add s3-backend.tf && git commit -m "Setup Terraform S3 backend" + git push + ``` + +## Destroy + +To remove the backend infrastructure, you can run the following command. Be cautious as this will remove the S3 bucket and the DynamoDB table used for state locking: + +```sh +terraform destroy +``` + +## Additional Notes + +- **Security Best Practices:** Ensure that the S3 bucket and DynamoDB table have strict access policies to protect your state files. +- **Documentation:** Keep documentation up to date and ensure all team members are aware of the backend configurations and how to handle state files securely. diff --git a/live/staging/us-west-2/app/backend.tf b/live/terraform-backend/backend.tf similarity index 72% rename from live/staging/us-west-2/app/backend.tf rename to live/terraform-backend/backend.tf index 0cff6d7..e1dbb34 100644 --- a/live/staging/us-west-2/app/backend.tf +++ b/live/terraform-backend/backend.tf @@ -6,14 +6,13 @@ module "terraform_state_backend" { source = "cloudposse/tfstate-backend/aws" version = "1.1.1" - name = module.label.name - namespace = module.label.namespace - environment = module.label.environment - attributes = ["state"] + name = var.name + namespace = var.namespace + attributes = ["state"] terraform_backend_config_file_path = "." terraform_backend_config_file_name = "s3-backend.tf" - terraform_state_file = "${module.label.id}.tfstate" + terraform_state_file = "${var.namespace}-${var.name}.tfstate" bucket_enabled = true dynamodb_enabled = true diff --git a/live/terraform-backend/configs/common-infra.tfvars b/live/terraform-backend/configs/common-infra.tfvars new file mode 100644 index 0000000..7c44863 --- /dev/null +++ b/live/terraform-backend/configs/common-infra.tfvars @@ -0,0 +1,8 @@ +# General settings + +region = "us-west-2" +name = "core" +namespace = "nan" +tags = { + "Terraform" = "true" +} diff --git a/live/terraform-backend/context.tf b/live/terraform-backend/context.tf new file mode 100644 index 0000000..7fb1402 --- /dev/null +++ b/live/terraform-backend/context.tf @@ -0,0 +1,26 @@ +variable "name" { + description = "Name to use for servers, tags, etc" + type = string + default = "name" +} + +variable "namespace" { + description = "Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp'" + type = string + default = "development" +} + +variable "tags" { + description = "Any extra tags to assign to objects" + type = map(any) + default = {} +} + +data "aws_caller_identity" "aws" {} + +locals { + tf_tags = { + Terraform = true, + By = data.aws_caller_identity.aws.arn + } +} diff --git a/live/staging/us-west-2/app/docs/MODULE.md b/live/terraform-backend/docs/MODULE.md similarity index 66% rename from live/staging/us-west-2/app/docs/MODULE.md rename to live/terraform-backend/docs/MODULE.md index a5e3ad2..94f63be 100644 --- a/live/staging/us-west-2/app/docs/MODULE.md +++ b/live/terraform-backend/docs/MODULE.md @@ -16,11 +16,11 @@ | Name | Source | Version | |------|--------|---------| -| [bastion](#module\_bastion) | ../../../../modules/bastion | n/a | -| [exampledb](#module\_exampledb) | ../../../../modules/rds | n/a | +| [bastion](#module\_bastion) | ../../modules/bastion | n/a | +| [exampledb](#module\_exampledb) | ../../modules/rds | n/a | | [label](#module\_label) | cloudposse/label/null | 0.25.0 | | [terraform\_state\_backend](#module\_terraform\_state\_backend) | cloudposse/tfstate-backend/aws | 1.1.1 | -| [vpc](#module\_vpc) | ../../../../modules/vpc | n/a | +| [vpc](#module\_vpc) | ../../modules/vpc | n/a | ## Resources @@ -30,7 +30,18 @@ ## Inputs -No inputs. +| Name | Description | Type | Default | Required | +|------|-------------|------|---------|:--------:| +| [enable\_bastion](#input\_enable\_bastion) | Enable bastion host | `bool` | `false` | no | +| [environment](#input\_environment) | Environment, e.g. 'prod', 'staging', 'dev', 'pre-prod', 'UAT' | `string` | `"development"` | no | +| [example\_db\_master\_username](#input\_example\_db\_master\_username) | The username for the master DB user | `string` | `"root"` | no | +| [example\_db\_name](#input\_example\_db\_name) | The name of the database to create | `string` | `"mydb"` | no | +| [name](#input\_name) | Name to use for servers, tags, etc | `string` | `"name"` | no | +| [namespace](#input\_namespace) | Namespace, which could be your organization name or abbreviation, e.g. 'eg' or 'cp' | `string` | `"development"` | no | +| [region](#input\_region) | AWS region | `string` | `"us-west-2"` | no | +| [stage](#input\_stage) | Stage, e.g. 'build', 'test', 'deploy', 'release' | `string` | `null` | no | +| [tags](#input\_tags) | Any extra tags to assign to objects | `map(any)` | `{}` | no | +| [vpc\_cidr\_block](#input\_vpc\_cidr\_block) | CIDR block for the VPC | `string` | `"10.0.0.0/16"` | no | ## Outputs diff --git a/live/staging/us-west-2/app/main.tf b/live/terraform-backend/main.tf similarity index 78% rename from live/staging/us-west-2/app/main.tf rename to live/terraform-backend/main.tf index fe5e8dc..143ee86 100644 --- a/live/staging/us-west-2/app/main.tf +++ b/live/terraform-backend/main.tf @@ -1,5 +1,5 @@ provider "aws" { - region = "us-west-2" + region = var.region default_tags { tags = { diff --git a/live/staging/us-west-2/app/outputs.tf b/live/terraform-backend/outputs.tf similarity index 100% rename from live/staging/us-west-2/app/outputs.tf rename to live/terraform-backend/outputs.tf diff --git a/live/terraform-backend/variables.tf b/live/terraform-backend/variables.tf new file mode 100644 index 0000000..098424d --- /dev/null +++ b/live/terraform-backend/variables.tf @@ -0,0 +1,5 @@ +variable "region" { + description = "AWS region" + type = string + default = "us-west-2" +} diff --git a/live/staging/us-west-2/app/versions.tf b/live/terraform-backend/versions.tf similarity index 100% rename from live/staging/us-west-2/app/versions.tf rename to live/terraform-backend/versions.tf