diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index f7ea64b7da4..41a2c2de965 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -2,6 +2,7 @@ # https://github.com/ministryofjustice/modernisation-platform/blob/main/scripts/provision-member-directories.sh * @ministryofjustice/modernisation-platform +/terraform/environments/apex @ministryofjustice/laa-aws-infrastructure @ministryofjustice/modernisation-platform /terraform/environments/equip @ministryofjustice/modernisation-platform-engineers @ministryofjustice/modernisation-platform /terraform/environments/example @ministryofjustice/modernisation-platform @ministryofjustice/modernisation-platform /terraform/environments/nomis @ministryofjustice/studio-webops @ministryofjustice/modernisation-platform diff --git a/.github/workflows/apex.yml b/.github/workflows/apex.yml new file mode 100644 index 00000000000..bc61cbfe970 --- /dev/null +++ b/.github/workflows/apex.yml @@ -0,0 +1,204 @@ +--- +name: apex +on: + push: + branches: + - main + paths: + - 'terraform/environments/apex/**' + - '.github/workflows/apex.yml' + pull_request: + branches: + - main + types: [opened, edited, reopened, synchronize] + paths: + - 'terraform/environments/apex/**' + - '.github/workflows/apex.yml' + workflow_dispatch: +env: + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + TF_IN_AUTOMATION: true +defaults: + run: + shell: bash + +jobs: + + # These jobs run when creating a pull request + plan-development: + name: Plan Development - apex + runs-on: ubuntu-latest + if: github.ref != 'refs/heads/main' || github.event_name == 'workflow_dispatch' + steps: + - name: Checkout Repository + uses: actions/checkout@v2.3.4 + - name: Load and Configure Terraform + uses: hashicorp/setup-terraform@v1.3.2 + with: + terraform_version: "~1" + terraform_wrapper: false + - name: Terraform plan - development + run: | + echo "Terraform plan - ${TF_ENV}" + bash scripts/terraform-init.sh terraform/environments/apex + terraform -chdir="terraform/environments/apex" workspace select "apex-${TF_ENV}" + bash scripts/terraform-plan.sh terraform/environments/apex + env: + TF_ENV: development + + deploy-development: + name: Deploy Development - apex + runs-on: ubuntu-latest + if: github.ref != 'refs/heads/main' || github.event_name == 'workflow_dispatch' + environment: + name: apex-development + steps: + - name: Checkout Repository + uses: actions/checkout@v2.3.4 + - name: Load and Configure Terraform + uses: hashicorp/setup-terraform@v1.3.2 + with: + terraform_version: "~1" + terraform_wrapper: false + - name: Terraform apply - development + run: | + echo "Terraform apply - ${TF_ENV}" + bash scripts/terraform-init.sh terraform/environments/apex + terraform -chdir="terraform/environments/apex" workspace select "apex-${TF_ENV}" + bash scripts/terraform-apply.sh terraform/environments/apex + env: + TF_ENV: development + + # plan-test: + # name: Plan Test - apex + # runs-on: ubuntu-latest + # if: github.ref != 'refs/heads/main' || github.event_name == 'workflow_dispatch' + # steps: + # - name: Checkout Repository + # uses: actions/checkout@v2.3.4 + # - name: Load and Configure Terraform + # uses: hashicorp/setup-terraform@v1.3.2 + # with: + # terraform_version: "~1" + # terraform_wrapper: false + # - name: Terraform plan - test + # run: | + # echo "Terraform plan - ${TF_ENV}" + # bash scripts/terraform-init.sh terraform/environments/apex + # terraform -chdir="terraform/environments/apex" workspace select "apex-${TF_ENV}" + # bash scripts/terraform-plan.sh terraform/environments/apex + # env: + # TF_ENV: test + # + # deploy-test: + # name: Deploy Test - apex + # runs-on: ubuntu-latest + # if: github.ref != 'refs/heads/main' || github.event_name == 'workflow_dispatch' + # environment: + # name: apex-test + # steps: + # - name: Checkout Repository + # uses: actions/checkout@v2.3.4 + # - name: Load and Configure Terraform + # uses: hashicorp/setup-terraform@v1.3.2 + # with: + # terraform_version: "~1" + # terraform_wrapper: false + # - name: Terraform apply - test + # run: | + # echo "Terraform apply - ${TF_ENV}" + # bash scripts/terraform-init.sh terraform/environments/apex + # terraform -chdir="terraform/environments/apex" workspace select "apex-${TF_ENV}" + # bash scripts/terraform-apply.sh terraform/environments/apex + # env: + # TF_ENV: test + # + # # These jobs run after merging to main + # plan-preproduction: + # name: Plan Preproduction - apex + # runs-on: ubuntu-latest + # if: github.ref == 'refs/heads/main' + # steps: + # - name: Checkout Repository + # uses: actions/checkout@v2.3.4 + # - name: Load and Configure Terraform + # uses: hashicorp/setup-terraform@v1.3.2 + # with: + # terraform_version: "~1" + # terraform_wrapper: false + # - name: Terraform plan - preproduction + # run: | + # echo "Terraform plan - ${TF_ENV}" + # bash scripts/terraform-init.sh terraform/environments/apex + # terraform -chdir="terraform/environments/apex" workspace select "apex-${TF_ENV}" + # bash scripts/terraform-plan.sh terraform/environments/apex + # env: + # TF_ENV: preproduction + # + # deploy-preproduction: + # name: Deploy Preproduction - apex + # runs-on: ubuntu-latest + # if: github.ref == 'refs/heads/main' + # environment: + # name: apex-preproduction + # steps: + # - name: Checkout Repository + # uses: actions/checkout@v2.3.4 + # - name: Load and Configure Terraform + # uses: hashicorp/setup-terraform@v1.3.2 + # with: + # terraform_version: "~1" + # terraform_wrapper: false + # - name: Terraform apply - preproduction + # run: | + # echo "Terraform apply - ${TF_ENV}" + # bash scripts/terraform-init.sh terraform/environments/apex + # terraform -chdir="terraform/environments/apex" workspace select "apex-${TF_ENV}" + # bash scripts/terraform-apply.sh terraform/environments/apex + # env: + # TF_ENV: preproduction + # + # plan-production: + # name: Plan Production - apex + # runs-on: ubuntu-latest + # if: github.ref == 'refs/heads/main' + # steps: + # - name: Checkout Repository + # uses: actions/checkout@v2.3.4 + # - name: Load and Configure Terraform + # uses: hashicorp/setup-terraform@v1.3.2 + # with: + # terraform_version: "~1" + # terraform_wrapper: false + # - name: Terraform plan - production + # run: | + # echo "Terraform plan - ${TF_ENV}" + # bash scripts/terraform-init.sh terraform/environments/apex + # terraform -chdir="terraform/environments/apex" workspace select "apex-${TF_ENV}" + # bash scripts/terraform-plan.sh terraform/environments/apex + # env: + # TF_ENV: production + # + # deploy-production: + # name: Deploy Production - apex + # runs-on: ubuntu-latest + # if: github.ref == 'refs/heads/main' + # environment: + # name: apex-production + # steps: + # - name: Checkout Repository + # uses: actions/checkout@v2.3.4 + # - name: Load and Configure Terraform + # uses: hashicorp/setup-terraform@v1.3.2 + # with: + # terraform_version: "~1" + # terraform_wrapper: false + # - name: Terraform apply - production + # run: | + # echo "Terraform apply - ${TF_ENV}" + # bash scripts/terraform-init.sh terraform/environments/apex + # terraform -chdir="terraform/environments/apex" workspace select "apex-${TF_ENV}" + # bash scripts/terraform-apply.sh terraform/environments/apex + # env: + # TF_ENV: production diff --git a/terraform/environments/apex/application_variables.json b/terraform/environments/apex/application_variables.json new file mode 100644 index 00000000000..6b52bfe9b30 --- /dev/null +++ b/terraform/environments/apex/application_variables.json @@ -0,0 +1,16 @@ +{ + "accounts": { + "development": { + "example_var": "dev-data" + }, + "test": { + "example_var": "test-data" + }, + "preproduction": { + "example_var": "preproduction-data" + }, + "production": { + "example_var": "production-data" + } + } +} diff --git a/terraform/environments/apex/backend.tf b/terraform/environments/apex/backend.tf new file mode 100644 index 00000000000..57ab958dcf3 --- /dev/null +++ b/terraform/environments/apex/backend.tf @@ -0,0 +1,13 @@ +# Backend +terraform { + # `backend` blocks do not support variables, so the following are hard-coded here: + # - S3 bucket name, which is created in modernisation-platform-account/s3.tf + backend "s3" { + acl = "bucket-owner-full-control" + bucket = "modernisation-platform-terraform-state" + encrypt = true + key = "terraform.tfstate" + region = "eu-west-2" + workspace_key_prefix = "environments/members/apex" # This will store the object as environments/members/apex/${workspace}/terraform.tfstate + } +} diff --git a/terraform/environments/apex/base_variables.tf b/terraform/environments/apex/base_variables.tf new file mode 100644 index 00000000000..d196e7a5f26 --- /dev/null +++ b/terraform/environments/apex/base_variables.tf @@ -0,0 +1,5 @@ +variable "networking" { + + type = list(any) + +} \ No newline at end of file diff --git a/terraform/environments/apex/data.tf b/terraform/environments/apex/data.tf new file mode 100644 index 00000000000..06aa42edf48 --- /dev/null +++ b/terraform/environments/apex/data.tf @@ -0,0 +1,147 @@ +# This data sources allows us to get the Modernisation Platform account information for use elsewhere +# (when we want to assume a role in the MP, for instance) +data "aws_organizations_organization" "root_account" {} + +# Get the environments file from the main repository +data "http" "environments_file" { + url = "https://raw.githubusercontent.com/ministryofjustice/modernisation-platform/main/environments/${local.application_name}.json" +} + +# Current account data +data "aws_region" "current" {} + +data "aws_caller_identity" "current" {} + +# VPC and subnet data +data "aws_vpc" "shared" { + tags = { + "Name" = "${var.networking[0].business-unit}-${local.environment}" + } +} + +data "aws_subnets" "shared-data" { + filter { + name = "vpc-id" + values = [data.aws_vpc.shared.id] + } + tags = { + Name = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-data*" + } +} + +data "aws_subnets" "private-public" { + filter { + name = "vpc-id" + values = [data.aws_vpc.shared.id] + } + tags = { + Name = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-private*" + } +} + +data "aws_subnets" "shared-public" { + filter { + name = "vpc-id" + values = [data.aws_vpc.shared.id] + } + tags = { + Name = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-public*" + } +} + +data "aws_subnet" "data_subnets_a" { + vpc_id = data.aws_vpc.shared.id + tags = { + "Name" = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-data-${data.aws_region.current.name}a" + } +} + +data "aws_subnet" "data_subnets_b" { + vpc_id = data.aws_vpc.shared.id + tags = { + "Name" = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-data-${data.aws_region.current.name}b" + } +} + +data "aws_subnet" "data_subnets_c" { + vpc_id = data.aws_vpc.shared.id + tags = { + "Name" = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-data-${data.aws_region.current.name}c" + } +} + +data "aws_subnet" "private_subnets_a" { + vpc_id = data.aws_vpc.shared.id + tags = { + "Name" = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-private-${data.aws_region.current.name}a" + } +} + +data "aws_subnet" "private_subnets_b" { + vpc_id = data.aws_vpc.shared.id + tags = { + "Name" = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-private-${data.aws_region.current.name}b" + } +} + +data "aws_subnet" "private_subnets_c" { + vpc_id = data.aws_vpc.shared.id + tags = { + "Name" = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-private-${data.aws_region.current.name}c" + } +} + +data "aws_subnet" "public_subnets_a" { + vpc_id = data.aws_vpc.shared.id + tags = { + Name = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-public-${data.aws_region.current.name}a" + } +} + +data "aws_subnet" "public_subnets_b" { + vpc_id = data.aws_vpc.shared.id + tags = { + Name = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-public-${data.aws_region.current.name}b" + } +} + +data "aws_subnet" "public_subnets_c" { + vpc_id = data.aws_vpc.shared.id + tags = { + Name = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}-public-${data.aws_region.current.name}c" + } +} + +# Route53 DNS data +data "aws_route53_zone" "external" { + provider = aws.core-vpc + + name = "${var.networking[0].business-unit}-${local.environment}.modernisation-platform.service.justice.gov.uk." + private_zone = false +} + +data "aws_route53_zone" "inner" { + provider = aws.core-vpc + + name = "${var.networking[0].business-unit}-${local.environment}.modernisation-platform.internal." + private_zone = true +} + +data "aws_route53_zone" "network-services" { + provider = aws.core-network-services + + name = "modernisation-platform.service.justice.gov.uk." + private_zone = false +} + +# State for core-network-services resource information +data "terraform_remote_state" "core_network_services" { + backend = "s3" + config = { + acl = "bucket-owner-full-control" + bucket = "modernisation-platform-terraform-state" + key = "environments/accounts/core-network-services/core-network-services-production/terraform.tfstate" + region = "eu-west-2" + encrypt = "true" + } +} diff --git a/terraform/environments/apex/locals.tf b/terraform/environments/apex/locals.tf new file mode 100644 index 00000000000..a62f3f74203 --- /dev/null +++ b/terraform/environments/apex/locals.tf @@ -0,0 +1,35 @@ +locals { + + application_name = "apex" + + environment_management = jsondecode(data.aws_secretsmanager_secret_version.environment_management.secret_string) + + # This takes the name of the Terraform workspace (e.g. core-vpc-production), strips out the application name (e.g. core-vpc), and checks if + # the string leftover is `-production`, if it isn't (e.g. core-vpc-non-production => -non-production) then it sets the var to false. + is-production = substr(terraform.workspace, length(local.application_name), length(terraform.workspace)) == "-production" + is-preproduction = substr(terraform.workspace, length(local.application_name), length(terraform.workspace)) == "-preproduction" + is-test = substr(terraform.workspace, length(local.application_name), length(terraform.workspace)) == "-test" + is-development = substr(terraform.workspace, length(local.application_name), length(terraform.workspace)) == "-development" + + # Merge tags from the environment json file with additional ones + tags = merge( + jsondecode(data.http.environments_file.response_body).tags, + { "is-production" = local.is-production }, + { "environment-name" = terraform.workspace }, + { "source-code" = "https://github.com/ministryofjustice/modernisation-platform-environments" } + ) + + environment = trimprefix(terraform.workspace, "${var.networking[0].application}-") + vpc_name = var.networking[0].business-unit + subnet_set = var.networking[0].set + vpc_all = "${local.vpc_name}-${local.environment}" + subnet_set_name = "${var.networking[0].business-unit}-${local.environment}-${var.networking[0].set}" + + is_live = [substr(terraform.workspace, length(local.application_name), length(terraform.workspace)) == "-production" || substr(terraform.workspace, length(local.application_name), length(terraform.workspace)) == "-preproduction" ? "live" : "non-live"] + provider_name = "core-vpc-${local.environment}" + + # environment specfic variables + # example usage: + # example_data = local.application_data.accounts[local.environment].example_var + application_data = fileexists("./application_variables.json") ? jsondecode(file("./application_variables.json")) : {} +} diff --git a/terraform/environments/apex/networking.auto.tfvars.json b/terraform/environments/apex/networking.auto.tfvars.json new file mode 100644 index 00000000000..bea162bc36e --- /dev/null +++ b/terraform/environments/apex/networking.auto.tfvars.json @@ -0,0 +1,9 @@ +{ + "networking": [ + { + "business-unit": "laa", + "set": "general", + "application": "apex" + } + ] +} diff --git a/terraform/environments/apex/platform_secrets.tf b/terraform/environments/apex/platform_secrets.tf new file mode 100644 index 00000000000..cebe0441c28 --- /dev/null +++ b/terraform/environments/apex/platform_secrets.tf @@ -0,0 +1,33 @@ +######################### Run Terraform via CICD ################################## +# Get secret by name for environment management +data "aws_secretsmanager_secret" "environment_management" { + provider = aws.modernisation-platform + name = "environment_management" +} + +# Get latest secret value with ID from above. This secret stores account IDs for the Modernisation Platform sub-accounts +data "aws_secretsmanager_secret_version" "environment_management" { + provider = aws.modernisation-platform + secret_id = data.aws_secretsmanager_secret.environment_management.id +} +######################### Run Terraform via CICD ################################## + + +######################### Run Terraform Plan Locally Only ################################## +# To run a Terraform Plan locally, uncomment this bottom section of code and comment out the top section + +# # Get secret by arn for environment management +# data "aws_ssm_parameter" "environment_management_arn" { +# name = "environment_management_arn" +# } + +# data "aws_secretsmanager_secret" "environment_management" { +# arn = data.aws_ssm_parameter.environment_management_arn.value +# } + +# # Get latest secret value with ID from above. This secret stores account IDs for the Modernisation Platform sub-accounts +# data "aws_secretsmanager_secret_version" "environment_management" { +# secret_id = data.aws_secretsmanager_secret.environment_management.id +# } + +######################### Run Terraform Plan Locally Only ################################## diff --git a/terraform/environments/apex/providers.tf b/terraform/environments/apex/providers.tf new file mode 100644 index 00000000000..02b9111c1f3 --- /dev/null +++ b/terraform/environments/apex/providers.tf @@ -0,0 +1,67 @@ +######################### Run Terraform via CICD ################################## +# AWS provider for the workspace you're working in (every resource will default to using this, unless otherwise specified) +provider "aws" { + region = "eu-west-2" + assume_role { + role_arn = "arn:aws:iam::${local.environment_management.account_ids[terraform.workspace]}:role/MemberInfrastructureAccess" + } +} + +# AWS provider for the Modernisation Platform, to get things from there if required +provider "aws" { + alias = "modernisation-platform" + region = "eu-west-2" + skip_get_ec2_platforms = true +} + +# AWS provider for core-vpc-, to share VPCs into this account +provider "aws" { + alias = "core-vpc" + region = "eu-west-2" + skip_get_ec2_platforms = true + assume_role { + role_arn = "arn:aws:iam::${local.environment_management.account_ids[local.provider_name]}:role/member-delegation-${local.vpc_name}-${local.environment}" + } +} + +# AWS provider for network services to enable dns entries for certificate validation to be created +provider "aws" { + alias = "core-network-services" + region = "eu-west-2" + skip_get_ec2_platforms = true + assume_role { + role_arn = "arn:aws:iam::${local.environment_management.account_ids["core-network-services-production"]}:role/modify-dns-records" + } +} +######################### Run Terraform via CICD ################################## + + +######################### Run Terraform Plan Locally Only ################################## +# To run a Terraform Plan locally, uncomment this bottom section of code and comment out the top section + +# provider "aws" { +# region = "eu-west-2" +# } + +# # AWS provider for core-vpc-, to share VPCs into this account +# provider "aws" { +# alias = "core-vpc" +# region = "eu-west-2" +# skip_get_ec2_platforms = true + +# assume_role { +# role_arn = "arn:aws:iam::${local.environment_management.account_ids[local.provider_name]}:role/member-delegation-read-only" +# } +# } + +# # AWS provider for network services to enable dns entries for certificate validation to be created +# provider "aws" { +# alias = "core-network-services" +# region = "eu-west-2" +# skip_get_ec2_platforms = true + +# assume_role { +# role_arn = "arn:aws:iam::${local.environment_management.account_ids["core-network-services-production"]}:role/read-dns-records" +# } +# } +######################### Run Terraform Plan Locally Only ################################## diff --git a/terraform/environments/apex/versions.tf b/terraform/environments/apex/versions.tf new file mode 100644 index 00000000000..6156b542b62 --- /dev/null +++ b/terraform/environments/apex/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_providers { + aws = { + version = "~> 4.0" + source = "hashicorp/aws" + } + } + required_version = "~> 1.0" +}