diff --git a/.github/workflows/deployment.yaml b/.github/workflows/deployment.yaml deleted file mode 100644 index 2c43faa..0000000 --- a/.github/workflows/deployment.yaml +++ /dev/null @@ -1,67 +0,0 @@ -name: Deploy to ECS - -on: - workflow_dispatch: - inputs: - environment: - description: 'The environment to deploy to' - required: true - type: choice - options: - - "" - - prod - -concurrency: - group: ${{ github.event.inputs.environment }}-deploy - cancel-in-progress: false - -permissions: - id-token: write - contents: read - -env: - aws_region: us-east-1 - environment: ${{ github.event.inputs.environment }} - owner: "skylight" - project: "dibbs-ce" - -jobs: - terraform: - name: Run Terraform - runs-on: ubuntu-latest - defaults: - run: - shell: bash - working-directory: ./terraform/implementation/ecs - steps: - - name: Check Out Changes - uses: actions/checkout@v4 - - - name: Setup Terraform - uses: hashicorp/setup-terraform@v3 - - - name: configure aws credentials - uses: aws-actions/configure-aws-credentials@v4 - with: - role-to-assume: ${{ secrets.AWS_ROLE_ARN }} - role-session-name: githubDeploymentWorkflow - aws-region: ${{ env.aws_region }} - - - name: Terraform - env: - ENVIRONMENT: ${{ env.environment }} - BUCKET: ${{ secrets.TFSTATE_BUCKET }} - DYNAMODB_TABLE: ${{ secrets.TFSTATE_DYNAMODB_TABLE }} - REGION: ${{ env.aws_region }} - OWNER: ${{ env.owner }} - PROJECT: ${{ env.project }} - shell: bash - run: | - echo "ENVIRONMENT=$ENVIRONMENT" >> .env - echo "BUCKET=$BUCKET" >> .env - echo "DYNAMODB_TABLE=$DYNAMODB_TABLE" >> .env - echo "REGION=$REGION" >> .env - echo "owner = \"$OWNER\"" >> $ENVIRONMENT.tfvars - echo "project = \"$PROJECT\"" >> $ENVIRONMENT.tfvars - echo "region = \"$REGION\"" >> $ENVIRONMENT.tfvars - ./deploy.sh -e $ENVIRONMENT --ci diff --git a/.github/workflows/deployment_apply.yaml b/.github/workflows/deployment_apply.yaml new file mode 100644 index 0000000..465f33f --- /dev/null +++ b/.github/workflows/deployment_apply.yaml @@ -0,0 +1,73 @@ +name: Terraform Apply +run-name: Terraform ${{ inputs.terraform_action }} ${{ inputs.workspace }} by @${{ github.actor }} + +on: + workflow_dispatch: + inputs: + workspace: + description: 'The workspace to terraform against' + required: true + type: choice + options: + - "" + - prod + +concurrency: + group: ${{ github.event.inputs.workspace }}-terraform + cancel-in-progress: false + +permissions: + id-token: write + contents: read + +env: + workspace: ${{ github.event.inputs.workspace }} + terraform_action: apply + +jobs: + terraform: + name: Run Terraform + runs-on: ubuntu-latest + defaults: + run: + shell: bash + # this may need to be updated if you change the directory you are working with + # ./terraform/implementation/dev || ./terraform/implementation/prod for example + # this practice is recommended to keep the terraform code organized while reducing the risk of conflicts + working-directory: ./terraform/implementation/ecs + steps: + - name: Check Out Changes + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: configure aws credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + role-session-name: githubDeploymentWorkflow + aws-region: ${{ vars.AWS_REGION }} + + - name: Terraform + env: + ACTION: ${{ env.terraform_action }} + BUCKET: ${{ secrets.TFSTATE_BUCKET }} + DYNAMODB_TABLE: ${{ secrets.TFSTATE_DYNAMODB_TABLE }} + OWNER: ${{ vars.OWNER }} + PROJECT: ${{ vars.PROJECT }} + REGION: ${{ vars.AWS_REGION }} + WORKSPACE: ${{ env.workspace }} + shell: bash + run: | + echo "owner = \"$OWNER\"" >> $WORKSPACE.tfvars + echo "project = \"$PROJECT\"" >> $WORKSPACE.tfvars + echo "region = \"$REGION\"" >> $WORKSPACE.tfvars + terraform init \ + -var-file="$WORKSPACE.tfvars" \ + -backend-config "bucket=$BUCKET" \ + -backend-config "dynamodb_table=$DYNAMODB_TABLE" \ + -backend-config "region=$REGION" \ + || (echo "terraform init failed, exiting..." && exit 1) + terraform workspace select "$WORKSPACE" + terraform apply -auto-approve -var-file="$WORKSPACE.tfvars" diff --git a/.github/workflows/deployment_plan.yaml b/.github/workflows/deployment_plan.yaml new file mode 100644 index 0000000..7b58332 --- /dev/null +++ b/.github/workflows/deployment_plan.yaml @@ -0,0 +1,73 @@ +name: Terraform Plan +run-name: Terraform ${{ inputs.terraform_action }} ${{ inputs.workspace }} by @${{ github.actor }} + +on: + workflow_dispatch: + inputs: + workspace: + description: 'The workspace to terraform against' + required: true + type: choice + options: + - "" + - prod + +concurrency: + group: ${{ github.event.inputs.workspace }}-terraform + cancel-in-progress: false + +permissions: + id-token: write + contents: read + +env: + workspace: ${{ github.event.inputs.workspace }} + terraform_action: plan + +jobs: + terraform: + name: Run Terraform + runs-on: ubuntu-latest + defaults: + run: + shell: bash + # this may need to be updated if you change the directory you are working with + # ./terraform/implementation/dev || ./terraform/implementation/prod for example + # this practice is recommended to keep the terraform code organized while reducing the risk of conflicts + working-directory: ./terraform/implementation/ecs + steps: + - name: Check Out Changes + uses: actions/checkout@v4 + + - name: Setup Terraform + uses: hashicorp/setup-terraform@v3 + + - name: configure aws credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_ROLE_ARN }} + role-session-name: githubDeploymentWorkflow + aws-region: ${{ vars.AWS_REGION }} + + - name: Terraform + env: + ACTION: ${{ env.terraform_action }} + BUCKET: ${{ secrets.TFSTATE_BUCKET }} + DYNAMODB_TABLE: ${{ secrets.TFSTATE_DYNAMODB_TABLE }} + OWNER: ${{ vars.OWNER }} + PROJECT: ${{ vars.PROJECT }} + REGION: ${{ vars.AWS_REGION }} + WORKSPACE: ${{ env.workspace }} + shell: bash + run: | + echo "owner = \"$OWNER\"" >> $WORKSPACE.tfvars + echo "project = \"$PROJECT\"" >> $WORKSPACE.tfvars + echo "region = \"$REGION\"" >> $WORKSPACE.tfvars + terraform init \ + -var-file="$WORKSPACE.tfvars" \ + -backend-config "bucket=$BUCKET" \ + -backend-config "dynamodb_table=$DYNAMODB_TABLE" \ + -backend-config "region=$REGION" \ + || (echo "terraform init failed, exiting..." && exit 1) + terraform workspace select "$WORKSPACE" + terraform plan -var-file="$WORKSPACE.tfvars" diff --git a/README.md b/README.md index fc00f25..1ed6272 100644 --- a/README.md +++ b/README.md @@ -193,9 +193,10 @@ The setup.sh script will create the following files: ## 4.6 Run Terraform Code In Your Designated Environment 4.6.1. Run ECS Module Locally -* To run your ECS Module Changes in your local terminal, navigate to _terraform/implementation/ecs/_ and run the following command: `cd /terraform/implementation`. +* It is highly recommended to create a new directory per environment that is launched, to do so run `cp terraform/implementation/ecs terraform/implementation/{insertEnvironmentName}`. +* To run your ECS Module Changes in your local terminal, navigate to your working directory, ` cd terraform/implementation/ecs/` or `cd terraform/implementation/{insertEnvironmentName}` * In your terminal run the deploy script for your designated environment `./deploy.sh -e {insertEnvironmentName}`.\ -    Note: The _-e_ tag stands for environment and you can specify `dev`, `stage`, `prod` +    Note: The _-e_ tag stands for environment and you can specify `dev`, `stage`, `prod`, this can match your environment naming convention.     or whatever environment your team desires. [Return to Table of Contents](#table-of-contents). @@ -226,7 +227,7 @@ The setup.sh script will create the following files: ## 4.10 Update Variables 4.10.1. Update Other Default Variables -* Navigate to the _defaults.tfvars_ file `cd terraform/implementation/ecs/default.tfvars`. +* Navigate to the _defaults.tfvars_ file ` cd terraform/implementation/ecs/` or `cd terraform/implementation/{insertEnvironmentName}`. * In this _defaults.tfvars_ file, you can update and override any other default values. 4.10.2. Test and Validate Your Changes diff --git a/terraform/implementation/ecs/README.md b/terraform/implementation/ecs/README.md index 42c09a7..7a66cfa 100644 --- a/terraform/implementation/ecs/README.md +++ b/terraform/implementation/ecs/README.md @@ -28,18 +28,17 @@ | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [availability\_zones](#input\_availability\_zones) | The availability zones to use | `list(string)` |
[
"us-east-1a",
"us-east-1b",
"us-east-1c"
]
| no | +| [availability\_zones](#input\_availability\_zones) | The availability zones to use | `list(string)` |
[
"us-east-1a",
"us-east-1b",
"us-east-1c"
]
| no | | [ecr\_viewer\_database\_schema](#input\_ecr\_viewer\_database\_schema) | The database schema used for the eCR data tables | `string` | `"core"` | no | | [ecr\_viewer\_database\_type](#input\_ecr\_viewer\_database\_type) | The SQL variant used for the eCR data tables | `string` | `"postgres"` | no | | [ecs\_alb\_sg](#input\_ecs\_alb\_sg) | The security group for the Application Load Balancer | `string` | `"ecs-albsg"` | no | -| [enable\_nat\_gateway](#input\_enable\_nat\_gateway) | Enable NAT Gateway | `bool` | `true` | no | +| [internal](#input\_internal) | Flag to determine if the several AWS resources are public (intended for external access, public internet) or private (only intended to be accessed within your AWS VPC or avaiable with other means, a transit gateway for example). | `bool` | `true` | no | | [owner](#input\_owner) | The owner of the infrastructure | `string` | `"skylight"` | no | | [phdi\_version](#input\_phdi\_version) | PHDI container image version | `string` | `"v1.4.4"` | no | -| [private\_subnets](#input\_private\_subnets) | The private subnets | `list(string)` |
[
"176.24.1.0/24",
"176.24.3.0/24"
]
| no | -| [project](#input\_project) | The project name | `string` | `"dibbs-ce"` | no | -| [public\_subnets](#input\_public\_subnets) | The public subnets | `list(string)` |
[
"176.24.2.0/24",
"176.24.4.0/24"
]
| no | +| [private\_subnets](#input\_private\_subnets) | The private subnets | `list(string)` |
[
"176.24.1.0/24",
"176.24.3.0/24"
]
| no | +| [project](#input\_project) | The project name | `string` | `"dibbs"` | no | +| [public\_subnets](#input\_public\_subnets) | The public subnets | `list(string)` |
[
"176.24.2.0/24",
"176.24.4.0/24"
]
| no | | [region](#input\_region) | AWS region | `string` | `"us-east-1"` | no | -| [single\_nat\_gateway](#input\_single\_nat\_gateway) | Single NAT Gateway | `bool` | `true` | no | | [vpc](#input\_vpc) | The name of the VPC | `string` | `"ecs-vpc"` | no | | [vpc\_cidr](#input\_vpc\_cidr) | The CIDR block for the VPC | `string` | `"176.24.0.0/16"` | no | diff --git a/terraform/implementation/ecs/SERVICEDATA.md b/terraform/implementation/ecs/SERVICEDATA.md index 2550905..a28d0ea 100644 --- a/terraform/implementation/ecs/SERVICEDATA.md +++ b/terraform/implementation/ecs/SERVICEDATA.md @@ -6,12 +6,14 @@ service_data = { short_name = "ecrv", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 - app_image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/${terraform.workspace}-ecr-viewer", + min_capacity = 1 + max_capacity = 5 + app_image = var.disable_ecr == false ? "${terraform.workspace}-ecr-viewer" : "ecr-viewer", app_version = var.phdi_version, container_port = 3000, host_port = 3000, public = true + registry_url = local.registry_url env_vars = [ { name = "AWS_REGION", @@ -24,6 +26,26 @@ service_data = { { name = "HOSTNAME", value = "0.0.0.0" + }, + { + name = "NEXT_PUBLIC_NON_INTEGRATED_VIEWER", + value = var.non_integrated_viewer + }, + { + name = "SOURCE", + value = "s3" + }, + { + name = "APP_ENV", + value = var.ecr_viewer_app_env + }, + { + name = "NBS_PUB_KEY", + value = var.ecr_viewer_auth_pub_key + }, + { + name = "NEXT_PUBLIC_BASEPATH", + value = var.ecr_viewer_basepath } ] }, @@ -31,79 +53,91 @@ service_data = { short_name = "fhirc", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 - app_image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/${terraform.workspace}-fhir-converter", + min_capacity = 1 + max_capacity = 5 + app_image = var.disable_ecr == false ? "${terraform.workspace}-fhir-converter" : "fhir-converter", app_version = var.phdi_version, container_port = 8080, host_port = 8080, public = false + registry_url = local.registry_url env_vars = [] }, ingestion = { short_name = "inge", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 - app_image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/${terraform.workspace}-ingestion", + min_capacity = 1 + max_capacity = 5 + app_image = var.disable_ecr == false ? "${terraform.workspace}-ingestion" : "ingestion", app_version = var.phdi_version, container_port = 8080, host_port = 8080, public = false + registry_url = local.registry_url env_vars = [] }, validation = { short_name = "vali", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 - app_image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/${terraform.workspace}-validation", + min_capacity = 1 + max_capacity = 5 + app_image = var.disable_ecr == false ? "${terraform.workspace}-validation" : "validation", app_version = var.phdi_version, container_port = 8080, host_port = 8080, public = false + registry_url = local.registry_url env_vars = [] }, trigger-code-reference = { short_name = "trigcr", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 - app_image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/${terraform.workspace}-trigger-code-reference", + min_capacity = 1 + max_capacity = 5 + app_image = var.disable_ecr == false ? "${terraform.workspace}-trigger-code-reference" : "trigger-code-reference", app_version = var.phdi_version, container_port = 8080, host_port = 8080, public = false + registry_url = local.registry_url env_vars = [] }, message-parser = { short_name = "msgp", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 - app_image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/${terraform.workspace}-message-parser", + min_capacity = 1 + max_capacity = 5 + app_image = var.disable_ecr == false ? "${terraform.workspace}-message-parser" : "message-parser", app_version = var.phdi_version, container_port = 8080, host_port = 8080, public = false + registry_url = local.registry_url env_vars = [] }, orchestration = { short_name = "orch", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 - app_image = "${data.aws_caller_identity.current.account_id}.dkr.ecr.${var.region}.amazonaws.com/${terraform.workspace}-orchestration", + min_capacity = 1 + max_capacity = 5 + app_image = var.disable_ecr == false ? "${terraform.workspace}-orchestration" : "orchestration", app_version = var.phdi_version, container_port = 8080, host_port = 8080, public = true + registry_url = local.registry_url env_vars = [ { - name = "OTEL_METRICS", + name = "OTEL_METRICS", value = "none" }, { - name = "OTEL_METRICS_EXPORTER", + name = "OTEL_METRICS_EXPORTER", value = "none" }, { @@ -120,7 +154,7 @@ service_data = { }, { name = "ECR_VIEWER_URL", - value = "http://ecr-viewer:3000" + value = "http://ecr-viewer:3000${var.ecr_viewer_basepath}" }, { name = "MESSAGE_PARSER_URL", diff --git a/terraform/implementation/ecs/_variable.tf b/terraform/implementation/ecs/_variable.tf index fe108a8..6eabe8b 100644 --- a/terraform/implementation/ecs/_variable.tf +++ b/terraform/implementation/ecs/_variable.tf @@ -4,18 +4,18 @@ variable "availability_zones" { default = ["us-east-1a", "us-east-1b", "us-east-1c"] } +variable "internal" { + description = "Flag to determine if the several AWS resources are public (intended for external access, public internet) or private (only intended to be accessed within your AWS VPC or avaiable with other means, a transit gateway for example)." + type = bool + default = true +} + variable "ecs_alb_sg" { description = "The security group for the Application Load Balancer" type = string default = "ecs-albsg" } -variable "enable_nat_gateway" { - description = "Enable NAT Gateway" - type = bool - default = true -} - variable "owner" { description = "The owner of the infrastructure" type = string @@ -38,7 +38,7 @@ variable "private_subnets" { variable "project" { description = "The project name" type = string - default = "dibbs-ce" + default = "dibbs" } variable "public_subnets" { @@ -53,12 +53,6 @@ variable "region" { default = "us-east-1" } -variable "single_nat_gateway" { - description = "Single NAT Gateway" - type = bool - default = true -} - variable "vpc" { description = "The name of the VPC" type = string diff --git a/terraform/implementation/ecs/deploy.sh b/terraform/implementation/ecs/deploy.sh index 3bd0827..d41e8b9 100755 --- a/terraform/implementation/ecs/deploy.sh +++ b/terraform/implementation/ecs/deploy.sh @@ -6,7 +6,7 @@ if [ -f .env ]; then fi # set default values -ENVIRONMENT="${ENVIRONMENT:-}" +WORKSPACE="${WORKSPACE:-}" BUCKET="${BUCKET:-}" DYNAMODB_TABLE="${DYNAMODB_TABLE:-}" REGION="${REGION:-}" @@ -20,7 +20,7 @@ do case $key in -env|--env|-e) - ENVIRONMENT="$2" + WORKSPACE="$2" shift shift ;; @@ -71,9 +71,9 @@ if ! command -v terraform &> /dev/null; then exit 1 fi -if [ -z "$ENVIRONMENT" ] || [ -z "$BUCKET" ] || [ -z "$DYNAMODB_TABLE" ] || [ -z "$REGION" ]; then +if [ -z "$WORKSPACE" ] || [ -z "$BUCKET" ] || [ -z "$DYNAMODB_TABLE" ] || [ -z "$REGION" ]; then echo "Missing required arguments. Please provide all the required arguments." - echo "ENVIRONMENT: $ENVIRONMENT" + echo "WORKSPACE: $WORKSPACE" echo "BUCKET: $BUCKET" echo "DYNAMODB_TABLE: $DYNAMODB_TABLE" echo "REGION: $REGION" @@ -82,41 +82,41 @@ if [ -z "$ENVIRONMENT" ] || [ -z "$BUCKET" ] || [ -z "$DYNAMODB_TABLE" ] || [ -z fi if [ "$CI" = false ]; then - if [ ! -f "$ENVIRONMENT.tfvars" ]; then - echo "Creating $ENVIRONMENT.tfvars" - touch "$ENVIRONMENT.tfvars" + if [ ! -f "$WORKSPACE.tfvars" ]; then + echo "Creating $WORKSPACE.tfvars" + touch "$WORKSPACE.tfvars" fi - if ! grep -q "owner" "$ENVIRONMENT.tfvars"; then + if ! grep -q "owner" "$WORKSPACE.tfvars"; then read -p "Who is the owner of this infrastructure? ( default=skylight ): " owner_choice owner_choice=${owner_choice:-skylight} - echo "owner = \"$owner_choice\"" >> "$ENVIRONMENT.tfvars" + echo "owner = \"$owner_choice\"" >> "$WORKSPACE.tfvars" fi - if ! grep -q "project" "$ENVIRONMENT.tfvars"; then - read -p "What is this project called? ( default=dibbs-ce ): " project_choice - project_choice=${project_choice:-dibbs-ce} - echo "project = \"$project_choice\"" >> "$ENVIRONMENT.tfvars" + if ! grep -q "project" "$WORKSPACE.tfvars"; then + read -p "What is this project called? ( default=dibbs ): " project_choice + project_choice=${project_choice:-dibbs} + echo "project = \"$project_choice\"" >> "$WORKSPACE.tfvars" fi - if ! grep -q "region" "$ENVIRONMENT.tfvars"; then + if ! grep -q "region" "$WORKSPACE.tfvars"; then read -p "What aws region are you setting up in? ( default=us-east-1 ): " region_choice region_choice=${region_choice:-us-east-1} - echo "region = \"$region_choice\"" >> "$ENVIRONMENT.tfvars" + echo "region = \"$region_choice\"" >> "$WORKSPACE.tfvars" fi fi echo "Running Terraform with the following variables:" -echo "Environment: $ENVIRONMENT" -echo "Terraform Workspace: $ENVIRONMENT" +echo "Environment: $WORKSPACE" +echo "Terraform Workspace: $WORKSPACE" echo "Bucket: $BUCKET" echo "DynamoDB Table: $DYNAMODB_TABLE" echo "Region: $REGION" -cat "$ENVIRONMENT.tfvars" +cat "$WORKSPACE.tfvars" echo "" terraform init \ - -var-file="$ENVIRONMENT.tfvars" \ + -var-file="$WORKSPACE.tfvars" \ -backend-config "bucket=$BUCKET" \ -backend-config "dynamodb_table=$DYNAMODB_TABLE" \ -backend-config "region=$REGION" \ @@ -124,27 +124,27 @@ terraform init \ # Check if workspace exists -if terraform workspace list | grep -q "$ENVIRONMENT"; then - echo "Selecting $ENVIRONMENT terraform workspace" - terraform workspace select "$ENVIRONMENT" +if terraform workspace list | grep -q "$WORKSPACE"; then + echo "Selecting $WORKSPACE terraform workspace" + terraform workspace select "$WORKSPACE" else if [ "$CI" = false ]; then - read -p "Workspace '$ENVIRONMENT' does not exist. Do you want to create it? (y/n): " choice + read -p "Workspace '$WORKSPACE' does not exist. Do you want to create it? (y/n): " choice if [[ $choice =~ ^[Yy]$ ]]; then - echo "Creating '$ENVIRONMENT' terraform workspace" - terraform workspace new "$ENVIRONMENT" + echo "Creating '$WORKSPACE' terraform workspace" + terraform workspace new "$WORKSPACE" else echo "Workspace creation cancelled." exit 1 fi else - echo "Creating '$ENVIRONMENT' terraform workspace" - terraform workspace new "$ENVIRONMENT" + echo "Creating '$WORKSPACE' terraform workspace" + terraform workspace new "$WORKSPACE" fi fi if [ "$CI" = false ]; then - terraform apply -var-file="$ENVIRONMENT.tfvars" + terraform apply -var-file="$WORKSPACE.tfvars" else - terraform apply -auto-approve -var-file="$ENVIRONMENT.tfvars" + terraform apply -auto-approve -var-file="$WORKSPACE.tfvars" fi diff --git a/terraform/implementation/ecs/main.tf b/terraform/implementation/ecs/main.tf index 254f717..b77ffbb 100644 --- a/terraform/implementation/ecs/main.tf +++ b/terraform/implementation/ecs/main.tf @@ -1,13 +1,15 @@ module "vpc" { source = "terraform-aws-modules/vpc/aws" - name = local.vpc_name - cidr = var.vpc_cidr - azs = var.availability_zones - private_subnets = var.private_subnets - public_subnets = var.public_subnets - enable_nat_gateway = var.enable_nat_gateway - single_nat_gateway = var.single_nat_gateway + name = local.vpc_name + cidr = var.vpc_cidr + azs = var.availability_zones + private_subnets = var.private_subnets + public_subnets = var.public_subnets + # if internal is true, then the VPC will not have a NAT or internet gateway + enable_nat_gateway = var.internal ? false : true + single_nat_gateway = var.internal ? false : true + create_igw = var.internal ? false : true tags = local.tags } @@ -18,14 +20,21 @@ module "ecs" { private_subnet_ids = flatten(module.vpc.private_subnets) vpc_id = module.vpc.vpc_id region = var.region - alb_internal = false owner = var.owner project = var.project tags = local.tags - + # If intent is to pull from the phdi GHCR, set disable_ecr to true (default is false) # disable_ecr = true - # If intent is to use the non-integrated viewer, set non_integrated_viewer to true (default is false) + + # If intent is to use the non-integrated viewer, set non_integrated_viewer to "true" (default is false) # non_integrated_viewer = "true" + + # If the intent is to make the ecr-viewer availabble on the public internet, set internal to false (default is true) + # This requires an internet gateway to be present in the VPC. + internal = var.internal + + # If the intent is to disable authentication, set ecr_viewer_app_env to "test" (default is "prod") + # ecr_viewer_app_env = "test" } diff --git a/terraform/implementation/setup/README.md b/terraform/implementation/setup/README.md index 4c2655d..da50e74 100644 --- a/terraform/implementation/setup/README.md +++ b/terraform/implementation/setup/README.md @@ -1,32 +1,28 @@ ## Requirements -No requirements. +| Name | Version | +|------|---------| +| [aws](#requirement\_aws) | =5.70.0 | ## Providers | Name | Version | |------|---------| -| [aws](#provider\_aws) | n/a | -| [local](#provider\_local) | n/a | -| [random](#provider\_random) | n/a | +| [local](#provider\_local) | 2.5.2 | +| [random](#provider\_random) | 3.6.3 | ## Modules | Name | Source | Version | |------|--------|---------| | [oidc](#module\_oidc) | ../../modules/oidc | n/a | +| [tfstate](#module\_tfstate) | ../../modules/tfstate | n/a | ## Resources | Name | Type | |------|------| -| [aws_dynamodb_table.tfstate_lock](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/dynamodb_table) | resource | -| [aws_s3_bucket.tfstate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource | -| [aws_s3_bucket_public_access_block.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource | -| [aws_s3_bucket_server_side_encryption_configuration.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource | -| [aws_s3_bucket_versioning.default](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource | -| [local_file.ecs_env](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | | [local_file.setup_env](https://registry.terraform.io/providers/hashicorp/local/latest/docs/resources/file) | resource | | [random_string.setup](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | @@ -36,7 +32,7 @@ No requirements. |------|-------------|------|---------|:--------:| | [oidc\_github\_repo](#input\_oidc\_github\_repo) | The GitHub repository for OIDC | `string` | `""` | no | | [owner](#input\_owner) | The owner of the project | `string` | `"skylight"` | no | -| [project](#input\_project) | The name of the project | `string` | `"dibbs-ce"` | no | +| [project](#input\_project) | The name of the project | `string` | `"dibbs"` | no | | [region](#input\_region) | The AWS region where resources are created | `string` | `"us-east-1"` | no | ## Outputs diff --git a/terraform/implementation/setup/_variable.tf b/terraform/implementation/setup/_variable.tf index 1f33a4b..48d81c0 100644 --- a/terraform/implementation/setup/_variable.tf +++ b/terraform/implementation/setup/_variable.tf @@ -13,7 +13,7 @@ variable "owner" { variable "project" { description = "The name of the project" type = string - default = "dibbs-ce" + default = "dibbs" } variable "region" { diff --git a/terraform/implementation/setup/backend.tf b/terraform/implementation/setup/backend.tf index 12c0dbe..17efe2e 100644 --- a/terraform/implementation/setup/backend.tf +++ b/terraform/implementation/setup/backend.tf @@ -1,3 +1,19 @@ terraform { backend "s3" {} + required_providers { + aws = { + source = "hashicorp/aws" + version = "=5.70.0" + } + } +} +provider "aws" { + region = "us-east-1" + default_tags { + tags = { + owner = "skylight" + environment = "tfstate" + project = "dibbs" + } + } } diff --git a/terraform/implementation/setup/main.tf b/terraform/implementation/setup/main.tf index e46a5ef..2d25ae0 100644 --- a/terraform/implementation/setup/main.tf +++ b/terraform/implementation/setup/main.tf @@ -1,14 +1,15 @@ -# Credentials should be provided by using the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables. -provider "aws" { - region = var.region - default_tags { - tags = { - owner = var.owner - workspace = terraform.workspace - project = var.project - id = random_string.setup.result - } - } +resource "random_string" "setup" { + length = 8 + special = false + upper = false +} + +module "tfstate" { + source = "../../modules/tfstate" + identifier = random_string.setup.result + owner = var.owner + project = var.project + region = var.region } # GitHub OIDC for prod @@ -26,79 +27,19 @@ module "oidc" { # This variable must match the name of the terraform workspace that you'll be using for your ECS module call in the /ecs module workspace = "prod" - state_bucket_arn = aws_s3_bucket.tfstate.arn - dynamodb_table_arn = aws_dynamodb_table.tfstate_lock.arn -} - -resource "random_string" "setup" { - length = 8 - special = false - upper = false -} - -resource "aws_s3_bucket" "tfstate" { - bucket = "${var.project}-tfstate-${var.owner}-${random_string.setup.result}" - - force_destroy = true -} - -resource "aws_s3_bucket_public_access_block" "default" { - bucket = aws_s3_bucket.tfstate.id - - block_public_acls = true - block_public_policy = true - ignore_public_acls = true - restrict_public_buckets = true -} - -# https://avd.aquasec.com/misconfig/aws/s3/avd-aws-0132/ -# trivy:ignore:AVD-AWS-0132 -resource "aws_s3_bucket_server_side_encryption_configuration" "default" { - bucket = aws_s3_bucket.tfstate.bucket - - rule { - apply_server_side_encryption_by_default { - sse_algorithm = "aws:kms" - } - } -} - -resource "aws_s3_bucket_versioning" "default" { - bucket = aws_s3_bucket.tfstate.id - versioning_configuration { - status = "Enabled" - } -} - -# Create a DynamoDB table for locking the state file -resource "aws_dynamodb_table" "tfstate_lock" { - name = "${var.project}-tfstate-lock-${var.owner}-${random_string.setup.result}" - hash_key = "LockID" - billing_mode = "PAY_PER_REQUEST" - - attribute { - name = "LockID" - type = "S" - } + # state_bucket_arn = module.tfstate.aws_s3_bucket.tfstate.arn + state_bucket_arn = module.tfstate.state_bucket.arn + # dynamodb_table_arn = aws_dynamodb_table.tfstate_lock.arn + dynamodb_table_arn = module.tfstate.dynamodb_table.arn } resource "local_file" "setup_env" { content = <<-EOT WORKSPACE="${terraform.workspace}" - BUCKET="${aws_s3_bucket.tfstate.bucket}" - DYNAMODB_TABLE="${aws_dynamodb_table.tfstate_lock.id}" + BUCKET="${module.tfstate.state_bucket.bucket}" + DYNAMODB_TABLE="${module.tfstate.dynamodb_table.arn}" REGION="${var.region}" TERRAFORM_ROLE="${module.oidc.role.arn}" EOT filename = ".env" -} - -resource "local_file" "ecs_env" { - content = <<-EOT - BUCKET="${aws_s3_bucket.tfstate.bucket}" - DYNAMODB_TABLE="${aws_dynamodb_table.tfstate_lock.id}" - REGION="${var.region}" - TERRAFORM_ROLE="${module.oidc.role.arn}" - EOT - filename = "../ecs/.env" -} +} \ No newline at end of file diff --git a/terraform/implementation/setup/setup.sh b/terraform/implementation/setup/setup.sh index f9800b2..bff48ac 100755 --- a/terraform/implementation/setup/setup.sh +++ b/terraform/implementation/setup/setup.sh @@ -2,6 +2,34 @@ WORKSPACE=tfstate +# write a function with aruments to set the backend +set_backend () { + region=$(grep "region" "$WORKSPACE.tfvars" | cut -d'=' -f2 | tr -d ' "') + owner=$(grep "owner" "$WORKSPACE.tfvars" | cut -d'=' -f2 | tr -d ' "') + project=$(grep "project" "$WORKSPACE.tfvars" | cut -d'=' -f2 | tr -d ' "') +cat > backend.tf <> "$WORKSPACE.tfvars" fi @@ -52,7 +80,7 @@ if ! grep -q "region" "$WORKSPACE.tfvars"; then fi if ! grep -q "oidc_github_repo" "$WORKSPACE.tfvars"; then - read -p "Do you want to setup a GitHub IODC role? (y/n): " github_choice + read -p "Do you want to setup a GitHub OIDC role? (y/n): " github_choice if [[ "$github_choice" =~ ^[Yy]$ ]]; then read -p "What is the organization/repo value for assume role? ( default=\"\" ): " repo_choice repo_choice=${repo_choice:-""} @@ -72,9 +100,7 @@ if [ "$USE_S3_BACKEND" == "true" ]; then -backend-config "dynamodb_table=$DYNAMODB_TABLE" \ -backend-config "region=$REGION" else -echo "terraform { - backend \"local\" {} -}" > backend.tf + set_backend "local" terraform init -var-file="$WORKSPACE.tfvars" fi @@ -92,13 +118,9 @@ terraform apply -var-file="$WORKSPACE.tfvars" if [ "$USE_S3_BACKEND" == "false" ]; then echo "Setting up your s3 terraform backend" if [ -f .env ]; then - export $(cat ../ecs/.env | xargs) + export $(cat .env | xargs) fi - -echo "terraform { - backend \"s3\" {} -}" > backend.tf - + set_backend "s3" terraform init \ -var-file="$WORKSPACE.tfvars" \ -migrate-state \ diff --git a/terraform/modules/ecs/README.md b/terraform/modules/ecs/README.md index 996be1c..afe73bb 100644 --- a/terraform/modules/ecs/README.md +++ b/terraform/modules/ecs/README.md @@ -51,6 +51,8 @@ No modules. | [aws_security_group_rule.ecs_all_egress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_security_group_rule.ecs_ecs_ingress](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group_rule) | resource | | [aws_service_discovery_private_dns_namespace.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/service_discovery_private_dns_namespace) | resource | +| [aws_vpc_endpoint.endpoints](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource | +| [aws_vpc_endpoint.s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/vpc_endpoint) | resource | | [dockerless_remote_image.dibbs](https://registry.terraform.io/providers/nullstone-io/dockerless/0.1.1/docs/resources/remote_image) | resource | | [null_resource.target_groups](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | | [random_string.s3_viewer](https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string) | resource | @@ -60,12 +62,12 @@ No modules. | [aws_iam_policy.ecs_task_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy) | data source | | [aws_iam_policy_document.assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.ecr_viewer_s3](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | +| [aws_route_table.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route_table) | data source | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| -| [alb\_internal](#input\_alb\_internal) | Flag to determine if the ALB is public (intended for external access) or private (only intended to be accessed within your AWS VPC). | `bool` | `true` | no | | [appmesh\_name](#input\_appmesh\_name) | Name of the AWS App Mesh | `string` | `""` | no | | [cloudmap\_namespace\_name](#input\_cloudmap\_namespace\_name) | Name of the AWS Cloud Map namespace | `string` | `""` | no | | [cloudmap\_service\_name](#input\_cloudmap\_service\_name) | Name of the AWS Cloud Map service | `string` | `""` | no | @@ -81,16 +83,17 @@ No modules. | [ecs\_cluster\_name](#input\_ecs\_cluster\_name) | Name of the ECS Cluster | `string` | `""` | no | | [ecs\_task\_execution\_role\_name](#input\_ecs\_task\_execution\_role\_name) | Name of the ECS Task Execution Role | `string` | `""` | no | | [ecs\_task\_role\_name](#input\_ecs\_task\_role\_name) | Name of the ECS Task Role | `string` | `""` | no | +| [internal](#input\_internal) | Flag to determine if the several AWS resources are public (intended for external access, public internet) or private (only intended to be accessed within your AWS VPC or avaiable with other means, a transit gateway for example). | `bool` | `true` | no | | [non\_integrated\_viewer](#input\_non\_integrated\_viewer) | A flag to determine if the viewer is the non-integrated version | `string` | `"false"` | no | | [owner](#input\_owner) | Owner of the resources | `string` | `"CDC"` | no | -| [phdi\_version](#input\_phdi\_version) | Version of the PHDI application | `string` | `"v1.6.4"` | no | +| [phdi\_version](#input\_phdi\_version) | Version of the PHDI application | `string` | `"v1.6.9"` | no | | [private\_subnet\_ids](#input\_private\_subnet\_ids) | List of private subnet IDs | `list(string)` | n/a | yes | | [project](#input\_project) | The project name | `string` | `"dibbs"` | no | | [public\_subnet\_ids](#input\_public\_subnet\_ids) | List of public subnet IDs | `list(string)` | n/a | yes | | [region](#input\_region) | The AWS region where resources are created | `string` | n/a | yes | | [s3\_viewer\_bucket\_name](#input\_s3\_viewer\_bucket\_name) | Name of the S3 bucket for the viewer | `string` | `""` | no | | [s3\_viewer\_bucket\_role\_name](#input\_s3\_viewer\_bucket\_role\_name) | Name of the IAM role for the ecr-viewer bucket | `string` | `""` | no | -| [service\_data](#input\_service\_data) | Data for the DIBBS services |
map(object({
short_name = string
fargate_cpu = number
fargate_memory = number
app_count = number
app_image = string
app_version = string
container_port = number
host_port = number
public = bool
registry_url = string
env_vars = list(object({
name = string
value = string
}))
}))
| `{}` | no | +| [service\_data](#input\_service\_data) | Data for the DIBBS services |
map(object({
short_name = string
fargate_cpu = number
fargate_memory = number
app_count = number
app_image = string
app_version = string
container_port = number
host_port = number
public = bool
registry_url = string
env_vars = list(object({
name = string
value = string
}))
}))
| `{}` | no | | [tags](#input\_tags) | Tags to apply to resources | `map(string)` | `{}` | no | | [vpc\_id](#input\_vpc\_id) | ID of the VPC | `string` | n/a | yes | diff --git a/terraform/modules/ecs/_data.tf b/terraform/modules/ecs/_data.tf index 84b5df3..4a51084 100644 --- a/terraform/modules/ecs/_data.tf +++ b/terraform/modules/ecs/_data.tf @@ -33,3 +33,8 @@ data "aws_iam_policy" "ecs_task_execution" { data "aws_iam_policy" "amazon_ec2_container_service_for_ec2_role" { name = "AmazonEC2ContainerServiceforEC2Role" } + +data "aws_route_table" "this" { + for_each = local.private_subnet_kvs + subnet_id = each.value +} \ No newline at end of file diff --git a/terraform/modules/ecs/_local.tf b/terraform/modules/ecs/_local.tf index af599ac..6d2c78d 100644 --- a/terraform/modules/ecs/_local.tf +++ b/terraform/modules/ecs/_local.tf @@ -14,7 +14,8 @@ locals { short_name = "ecrv", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 + min_capacity = 1 + max_capacity = 5 app_image = var.disable_ecr == false ? "${terraform.workspace}-ecr-viewer" : "ecr-viewer", app_version = var.phdi_version, container_port = 3000, @@ -60,7 +61,8 @@ locals { short_name = "fhirc", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 + min_capacity = 1 + max_capacity = 5 app_image = var.disable_ecr == false ? "${terraform.workspace}-fhir-converter" : "fhir-converter", app_version = var.phdi_version, container_port = 8080, @@ -73,7 +75,8 @@ locals { short_name = "inge", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 + min_capacity = 1 + max_capacity = 5 app_image = var.disable_ecr == false ? "${terraform.workspace}-ingestion" : "ingestion", app_version = var.phdi_version, container_port = 8080, @@ -86,7 +89,8 @@ locals { short_name = "vali", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 + min_capacity = 1 + max_capacity = 5 app_image = var.disable_ecr == false ? "${terraform.workspace}-validation" : "validation", app_version = var.phdi_version, container_port = 8080, @@ -99,7 +103,8 @@ locals { short_name = "trigcr", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 + min_capacity = 1 + max_capacity = 5 app_image = var.disable_ecr == false ? "${terraform.workspace}-trigger-code-reference" : "trigger-code-reference", app_version = var.phdi_version, container_port = 8080, @@ -112,7 +117,8 @@ locals { short_name = "msgp", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 + min_capacity = 1 + max_capacity = 5 app_image = var.disable_ecr == false ? "${terraform.workspace}-message-parser" : "message-parser", app_version = var.phdi_version, container_port = 8080, @@ -125,7 +131,8 @@ locals { short_name = "orch", fargate_cpu = 1024, fargate_memory = 2048, - app_count = 1 + min_capacity = 1 + max_capacity = 5 app_image = var.disable_ecr == false ? "${terraform.workspace}-orchestration" : "orchestration", app_version = var.phdi_version, container_port = 8080, @@ -183,4 +190,14 @@ locals { s3_viewer_bucket_name = var.s3_viewer_bucket_name == "" ? "${local.local_name}-${random_string.s3_viewer.result}" : var.s3_viewer_bucket_name s3_viewer_bucket_role_name = var.s3_viewer_bucket_role_name == "" ? "${local.local_name}-ecrv" : var.s3_viewer_bucket_role_name tags = var.tags + vpc_endpoints = [ + "com.amazonaws.${var.region}.ecr.dkr", + "com.amazonaws.${var.region}.ecr.api", + "com.amazonaws.${var.region}.ecs", + "com.amazonaws.${var.region}.ecs-telemetry", + "com.amazonaws.${var.region}.logs", + "com.amazonaws.${var.region}.secretsmanager", + ] + s3_service_name = "com.amazonaws.${var.region}.s3" + private_subnet_kvs = { for index, rt in var.private_subnet_ids : index => rt } } diff --git a/terraform/modules/ecs/_variable.tf b/terraform/modules/ecs/_variable.tf index 04f8834..14ad01f 100644 --- a/terraform/modules/ecs/_variable.tf +++ b/terraform/modules/ecs/_variable.tf @@ -1,6 +1,6 @@ -variable "alb_internal" { +variable "internal" { type = bool - description = "Flag to determine if the ALB is public (intended for external access) or private (only intended to be accessed within your AWS VPC)." + description = "Flag to determine if the several AWS resources are public (intended for external access, public internet) or private (only intended to be accessed within your AWS VPC or avaiable with other means, a transit gateway for example)." default = true } variable "appmesh_name" { diff --git a/terraform/modules/ecs/alb.tf b/terraform/modules/ecs/alb.tf index 31bbe12..c036e0f 100644 --- a/terraform/modules/ecs/alb.tf +++ b/terraform/modules/ecs/alb.tf @@ -1,10 +1,8 @@ -# https://avd.aquasec.com/misconfig/aws/elb/avd-aws-0053 -# trivy:ignore:AVD-AWS-0053 resource "aws_alb" "ecs" { name = local.ecs_alb_name - internal = var.alb_internal + internal = var.internal load_balancer_type = "application" - subnets = flatten([var.public_subnet_ids]) + subnets = var.internal == true ? flatten([var.private_subnet_ids]) : flatten([var.public_subnet_ids]) security_groups = [aws_security_group.alb.id] drop_invalid_header_fields = true @@ -36,6 +34,26 @@ resource "aws_alb_target_group" "this" { tags = local.tags } +resource "aws_vpc_endpoint" "endpoints" { + count = var.internal == true ? length(local.vpc_endpoints) : 0 + vpc_id = var.vpc_id + vpc_endpoint_type = "Interface" + private_dns_enabled = true + service_name = local.vpc_endpoints[count.index] + security_group_ids = [aws_security_group.ecs.id] + subnet_ids = flatten([var.private_subnet_ids]) + tags = local.tags +} + +resource "aws_vpc_endpoint" "s3" { + count = var.internal == true ? 1 : 0 + vpc_id = var.vpc_id + vpc_endpoint_type = "Gateway" + route_table_ids = [for rt in data.aws_route_table.this : rt.id] + service_name = local.s3_service_name + tags = local.tags +} + # The aws_alb_listener and aws_alb_listener_rule resources are not depended on by other resources so # they can be implemented via a loop or hard coded depending ease of maintenance # I've chosen the ways that reduce duplicated resource blocks: hard coded listener (i.e. http), looped listener rule (i.e. this) diff --git a/terraform/modules/ecs/ecs.tf b/terraform/modules/ecs/ecs.tf index a09785f..a3ba35b 100644 --- a/terraform/modules/ecs/ecs.tf +++ b/terraform/modules/ecs/ecs.tf @@ -43,7 +43,7 @@ resource "aws_ecs_service" "this" { name = each.key cluster = aws_ecs_cluster.dibbs_app_cluster.id task_definition = each.value.arn - desired_count = local.service_data[each.key].app_count + desired_count = local.service_data[each.key].min_capacity launch_type = "FARGATE" scheduling_strategy = "REPLICA" @@ -108,5 +108,10 @@ resource "aws_ecs_service" "this" { } } } + + lifecycle { + ignore_changes = [desired_count] + } + tags = local.tags } diff --git a/terraform/modules/oidc/_data.tf b/terraform/modules/oidc/_data.tf index 5cda2aa..3081936 100644 --- a/terraform/modules/oidc/_data.tf +++ b/terraform/modules/oidc/_data.tf @@ -51,6 +51,8 @@ data "aws_iam_policy_document" "wildcard" { statement { actions = [ "ec2:DescribeAddresses", + "ec2:DescribeVpcEndpoints", + "ec2:DescribePrefixLists", "ec2:DescribeAddressesAttribute", "ec2:DescribeFlowLogs", "ec2:DescribeInternetGateways", @@ -134,6 +136,7 @@ data "aws_iam_policy_document" "scoped_one" { data "aws_iam_policy_document" "scoped_two" { statement { actions = [ + "ec2:createVpcEndpoint", "ec2:CreateFlowLogs", "ec2:CreateNatGateway", "ec2:CreateNetworkAclEntry", @@ -155,6 +158,7 @@ data "aws_iam_policy_document" "scoped_two" { "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:natgateway/*", "arn:aws:ecr:${var.region}:${data.aws_caller_identity.current.account_id}:repository/*", "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/${local.project_owner_workspace}*", + "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:vpc-endpoint/*", ] } } @@ -164,6 +168,7 @@ data "aws_iam_policy_document" "request_tags_create_actions" { statement { actions = [ "appmesh:CreateMesh", + "ec2:createVpcEndpoint", "appmesh:CreateVirtualNode", "appmesh:DeleteMesh", "appmesh:DeleteVirtualNode", @@ -184,12 +189,14 @@ data "aws_iam_policy_document" "request_tags_create_actions" { "iam:CreateRole", "logs:CreateLogDelivery", "logs:CreateLogGroup", + "logs:TagResource", "servicediscovery:CreatePrivateDnsNamespace", ] resources = [ "arn:aws:appmesh:${var.region}:${data.aws_caller_identity.current.account_id}:mesh/${local.project_owner_workspace}", "arn:aws:appmesh:${var.region}:${data.aws_caller_identity.current.account_id}:mesh/${local.project_owner_workspace}/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:vpc/${local.vpc_id}", + "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:vpc-endpoint/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:vpc-flow-log/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:subnet/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:route-table/*", @@ -255,6 +262,7 @@ data "aws_iam_policy_document" "resource_tags_update_actions" { "iam:TagPolicy", "iam:UntagPolicy", "logs:PutRetentionPolicy", + "logs:UntagResource", "servicediscovery:TagResource", ] resources = [ @@ -304,6 +312,7 @@ data "aws_iam_policy_document" "resource_tags_delete_actions" { "ecs:DeleteCluster", "ecs:DeleteService", "ec2:DeleteVpc", + "ec2:DeleteVpcEndpoints", "ec2:DeleteTags", "ec2:DisassociateRouteTable", "ec2:DeleteRouteTable", @@ -331,6 +340,7 @@ data "aws_iam_policy_document" "resource_tags_delete_actions" { "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:natgateway/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:security-group/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:vpc-flow-log/*", + "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:vpc-endpoint/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:internet-gateway/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:elastic-ip/*", "arn:aws:ec2:${var.region}:${data.aws_caller_identity.current.account_id}:network-interface/*", @@ -356,4 +366,4 @@ data "aws_iam_policy_document" "resource_tags_delete_actions" { ] } } -} +} \ No newline at end of file diff --git a/terraform/modules/oidc/_variable.tf b/terraform/modules/oidc/_variable.tf index 91b2d7e..4f8a8a5 100644 --- a/terraform/modules/oidc/_variable.tf +++ b/terraform/modules/oidc/_variable.tf @@ -2,24 +2,36 @@ variable "oidc_github_repo" { description = "The GitHub repository for OIDC" type = string default = "" + validation { + condition = length(var.oidc_github_repo) == 0 || can(regex("^[a-zA-Z0-9-]+/[a-zA-Z0-9-]+$", var.oidc_github_repo)) + error_message = "oidc_github_repo must be set with 'org/repo' format or blank" + } } variable "owner" { description = "The owner of the project" type = string default = "skylight" + validation { + condition = can(regex("^[[:alnum:]]{1,8}$", var.owner)) + error_message = "owner must be 8 characters or less, all lowercase with no special characters or spaces" + } } variable "project" { description = "The name of the project" type = string - default = "dibbs-ce" + default = "dibbs" } variable "region" { type = string description = "The AWS region where resources are created" default = "" + validation { + condition = can(regex("^(us|eu|ap|sa|ca|cn|af|me|eu)-[[:alnum:]]{2,10}-[0-9]$", var.region)) + error_message = "region must be a valid AWS region" + } } variable "workspace" { diff --git a/terraform/modules/tfstate/_output.tf b/terraform/modules/tfstate/_output.tf new file mode 100644 index 0000000..c86c10a --- /dev/null +++ b/terraform/modules/tfstate/_output.tf @@ -0,0 +1,7 @@ +output "state_bucket" { + value = aws_s3_bucket.tfstate +} + +output "dynamodb_table" { + value = aws_dynamodb_table.tfstate_lock +} \ No newline at end of file diff --git a/terraform/modules/tfstate/_variable.tf b/terraform/modules/tfstate/_variable.tf new file mode 100644 index 0000000..514d742 --- /dev/null +++ b/terraform/modules/tfstate/_variable.tf @@ -0,0 +1,34 @@ +variable "owner" { + description = "The owner of the project" + type = string + default = "skylight" + validation { + condition = can(regex("^[[:alnum:]]{1,8}$", var.owner)) + error_message = "owner must be 8 characters/numbers or less, all lowercase with no special characters or spaces" + } +} + +variable "project" { + description = "The name of the project" + type = string + default = "dibbs" +} + +variable "region" { + type = string + description = "The AWS region where resources are created" + default = "" + validation { + condition = can(regex("^(us)-[[:alnum:]]{2,10}-[0-9]$", var.region)) + error_message = "region must be a valid AWS region" + } +} + +variable "identifier" { + type = string + default = "" + validation { + condition = can(regex("^[[:alnum:]]{1,8}$", var.identifier)) + error_message = "identifier must be 8 characters or less, all lowercase with no special characters or spaces" + } +} \ No newline at end of file diff --git a/terraform/modules/tfstate/main.tf b/terraform/modules/tfstate/main.tf new file mode 100644 index 0000000..de4e477 --- /dev/null +++ b/terraform/modules/tfstate/main.tf @@ -0,0 +1,45 @@ +resource "aws_s3_bucket" "tfstate" { + bucket = "${var.project}-tfstate-${var.owner}-${var.identifier}" + + force_destroy = true +} + +resource "aws_s3_bucket_public_access_block" "default" { + bucket = aws_s3_bucket.tfstate.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +# https://avd.aquasec.com/misconfig/aws/s3/avd-aws-0132/ +# trivy:ignore:AVD-AWS-0132 +resource "aws_s3_bucket_server_side_encryption_configuration" "default" { + bucket = aws_s3_bucket.tfstate.bucket + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "aws:kms" + } + } +} + +resource "aws_s3_bucket_versioning" "default" { + bucket = aws_s3_bucket.tfstate.id + versioning_configuration { + status = "Enabled" + } +} + +# Create a DynamoDB table for locking the state file +resource "aws_dynamodb_table" "tfstate_lock" { + name = "${var.project}-tfstate-lock-${var.owner}-${var.identifier}" + hash_key = "LockID" + billing_mode = "PAY_PER_REQUEST" + + attribute { + name = "LockID" + type = "S" + } +}