diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 41069d7166b..f3311b0a481 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -42,6 +42,7 @@ /terraform/environments/nomis-data-hub @ministryofjustice/studio-webops @ministryofjustice/modernisation-platform /terraform/environments/nomis @ministryofjustice/studio-webops @ministryofjustice/syscon-devs @ministryofjustice/syscon-nomis @ministryofjustice/modernisation-platform /terraform/environments/oas @ministryofjustice/laa-aws-infrastructure @ministryofjustice/oas-migration @ministryofjustice/modernisation-platform +/terraform/environments/oasys-national-reporting @ministryofjustice/studio-webops @ministryofjustice/modernisation-platform /terraform/environments/oasys @ministryofjustice/oasys-operator @ministryofjustice/studio-webops @ministryofjustice/modernisation-platform /terraform/environments/observability-platform @ministryofjustice/observability-platform @ministryofjustice/modernisation-platform /terraform/environments/performance-hub @ministryofjustice/performance-hub-developers @ministryofjustice/modernisation-platform diff --git a/.github/workflows/oasys-national-reporting.yml b/.github/workflows/oasys-national-reporting.yml new file mode 100644 index 00000000000..d7f46fb173c --- /dev/null +++ b/.github/workflows/oasys-national-reporting.yml @@ -0,0 +1,66 @@ +--- +name: oasys-national-reporting +on: + push: + branches: + - main + paths: + - 'terraform/environments/oasys-national-reporting/**' + - '.github/workflows/oasys-national-reporting.yml' + + pull_request: + branches: + - main + types: [opened, edited, reopened, synchronize] + paths: + - 'terraform/environments/oasys-national-reporting/**' + - '.github/workflows/oasys-national-reporting.yml' + + workflow_dispatch: + inputs: + action: + description: 'Set either [deploy|destroy].' + default: 'deploy' + required: true + type: string + options: + - deploy + - destroy + +permissions: + id-token: write # This is required for requesting the JWT + contents: read # This is required for actions/checkout + +jobs: + strategy: + uses: ./.github/workflows/reusable_terraform_strategy.yml + if: inputs.action != 'destroy' + with: + application: "${{ github.workflow }}" + + terraform: + needs: strategy + if: inputs.action != 'destroy' + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.strategy.outputs.matrix) }} + uses: ./.github/workflows/reusable_terraform_plan_apply.yml + with: + application: "${{ github.workflow }}" + environment: "${{ matrix.target }}" + action: "${{ matrix.action }}" + secrets: + modernisation_platform_environments: "${{ secrets.MODERNISATION_PLATFORM_ENVIRONMENTS }}" + pipeline_github_token: "${{ secrets.MODERNISATION_PLATFORM_CI_USER_ENVIRONMENTS_REPO_PAT }}" + + destroy-development: + if: inputs.action == 'destroy' + uses: ./.github/workflows/reusable_terraform_plan_apply.yml + with: + application: "${{ github.workflow }}" + environment: "development" + action: "plan_apply" + plan_apply_tfargs: "-destroy" + secrets: + modernisation_platform_environments: "${{ secrets.MODERNISATION_PLATFORM_ENVIRONMENTS }}" + pipeline_github_token: "${{ secrets.MODERNISATION_PLATFORM_CI_USER_ENVIRONMENTS_REPO_PAT }}" diff --git a/terraform/environments/oasys-national-reporting/README.md b/terraform/environments/oasys-national-reporting/README.md new file mode 100644 index 00000000000..9aa2658704c --- /dev/null +++ b/terraform/environments/oasys-national-reporting/README.md @@ -0,0 +1,76 @@ +# Service Runbook + + + +_If you have any questions surrounding this page please post in the `#team-name` channel._ + +## Mandatory Information + +### **Last review date:** + + + +### **Description:** + + + +### **Service URLs:** + + + +### **Incident response hours:** + + + +### **Incident contact details:** + + + +### **Service team contact:** + + + +### **Hosting environment:** + +Modernisation Platform + + + +## Optional + +### **Other URLs:** + + + +### **Expected speed and frequency of releases:** + + + +### **Automatic alerts:** + + + +### **Impact of an outage:** + + + +### **Out of hours response types:** + + + +### **Consumers of this service:** + + + +### **Services consumed by this:** + + + +### **Restrictions on access:** + + + +### **How to resolve specific issues:** + + diff --git a/terraform/environments/oasys-national-reporting/application_variables.json b/terraform/environments/oasys-national-reporting/application_variables.json new file mode 100644 index 00000000000..6b52bfe9b30 --- /dev/null +++ b/terraform/environments/oasys-national-reporting/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/oasys-national-reporting/data.tf b/terraform/environments/oasys-national-reporting/data.tf new file mode 100644 index 00000000000..96a2521d17e --- /dev/null +++ b/terraform/environments/oasys-national-reporting/data.tf @@ -0,0 +1 @@ +#### This file can be used to store data specific to the member account #### diff --git a/terraform/environments/oasys-national-reporting/locals.tf b/terraform/environments/oasys-national-reporting/locals.tf new file mode 100644 index 00000000000..a7454414911 --- /dev/null +++ b/terraform/environments/oasys-national-reporting/locals.tf @@ -0,0 +1 @@ +#### This file can be used to store locals specific to the member account #### diff --git a/terraform/environments/oasys-national-reporting/networking.auto.tfvars.json b/terraform/environments/oasys-national-reporting/networking.auto.tfvars.json new file mode 100644 index 00000000000..d3734bdbbf4 --- /dev/null +++ b/terraform/environments/oasys-national-reporting/networking.auto.tfvars.json @@ -0,0 +1,9 @@ +{ + "networking": [ + { + "business-unit": "", + "set": "", + "application": "oasys-national-reporting" + } + ] +} diff --git a/terraform/environments/oasys-national-reporting/platform_backend.tf b/terraform/environments/oasys-national-reporting/platform_backend.tf new file mode 100644 index 00000000000..9c1faf3ae9d --- /dev/null +++ b/terraform/environments/oasys-national-reporting/platform_backend.tf @@ -0,0 +1,14 @@ +# 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" + dynamodb_table = "modernisation-platform-terraform-state-lock" + encrypt = true + key = "terraform.tfstate" + region = "eu-west-2" + workspace_key_prefix = "environments/members/oasys-national-reporting" # This will store the object as environments/members/oasys-national-reporting/${workspace}/terraform.tfstate + } +} diff --git a/terraform/environments/oasys-national-reporting/platform_base_variables.tf b/terraform/environments/oasys-national-reporting/platform_base_variables.tf new file mode 100644 index 00000000000..e5713ed9493 --- /dev/null +++ b/terraform/environments/oasys-national-reporting/platform_base_variables.tf @@ -0,0 +1,11 @@ +variable "networking" { + + type = list(any) + +} + +variable "collaborator_access" { + type = string + default = "developer" + description = "Collaborators must specify which access level they are using, eg set an environment variable of export TF_VAR_collaborator_access=migration" +} diff --git a/terraform/environments/oasys-national-reporting/platform_data.tf b/terraform/environments/oasys-national-reporting/platform_data.tf new file mode 100644 index 00000000000..9844360a8cd --- /dev/null +++ b/terraform/environments/oasys-national-reporting/platform_data.tf @@ -0,0 +1,173 @@ +# 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" "shared-private" { + 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 +} + +# Shared KMS keys (per business unit) +data "aws_kms_key" "general_shared" { + key_id = "arn:aws:kms:eu-west-2:${local.environment_management.account_ids["core-shared-services-production"]}:alias/general-${var.networking[0].business-unit}" +} + +data "aws_kms_key" "ebs_shared" { + key_id = "arn:aws:kms:eu-west-2:${local.environment_management.account_ids["core-shared-services-production"]}:alias/ebs-${var.networking[0].business-unit}" +} + +data "aws_kms_key" "rds_shared" { + key_id = "arn:aws:kms:eu-west-2:${local.environment_management.account_ids["core-shared-services-production"]}:alias/rds-${var.networking[0].business-unit}" +} + +# 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" + } +} + +data "aws_organizations_organization" "root_account" {} + +# Retrieve information about the modernisation platform account +data "aws_caller_identity" "modernisation_platform" { + provider = aws.modernisation-platform +} + +# caller account information to instantiate aws.oidc provider +data "aws_caller_identity" "original_session" { + provider = aws.original-session +} + +data "aws_iam_session_context" "whoami" { + provider = aws.original-session + arn = data.aws_caller_identity.original_session.arn +} + +# 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" +} diff --git a/terraform/environments/oasys-national-reporting/platform_locals.tf b/terraform/environments/oasys-national-reporting/platform_locals.tf new file mode 100644 index 00000000000..0cac1bd762d --- /dev/null +++ b/terraform/environments/oasys-national-reporting/platform_locals.tf @@ -0,0 +1,38 @@ +locals { + + application_name = "oasys-national-reporting" + + environment_management = jsondecode(data.aws_secretsmanager_secret_version.environment_management.secret_string) + + # Stores modernisation platform account id for setting up the modernisation-platform provider + modernisation_platform_account_id = data.aws_ssm_parameter.modernisation_platform_account_id.value + + # 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")) : null +} diff --git a/terraform/environments/oasys-national-reporting/platform_providers.tf b/terraform/environments/oasys-national-reporting/platform_providers.tf new file mode 100644 index 00000000000..828f987c7aa --- /dev/null +++ b/terraform/environments/oasys-national-reporting/platform_providers.tf @@ -0,0 +1,58 @@ +# AWS provider for the original session which you connect with +provider "aws" { + alias = "original-session" + region = "eu-west-2" +} + +# 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 = !can(regex("githubactionsrolesession|AdministratorAccess|user", data.aws_caller_identity.original_session.arn)) ? null : can(regex("user", data.aws_caller_identity.original_session.arn)) ? "arn:aws:iam::${local.environment_management.account_ids[terraform.workspace]}:role/${var.collaborator_access}" : "arn:aws:iam::${data.aws_caller_identity.original_session.id}:role/MemberInfrastructureAccess" + } +} + +# AWS provider for the Modernisation Platform, to get things from there if required +provider "aws" { + alias = "modernisation-platform" + region = "eu-west-2" + assume_role { + role_arn = "arn:aws:iam::${local.modernisation_platform_account_id}:role/modernisation-account-limited-read-member-access" + } +} + +# AWS provider for core-vpc-, to access resources in the core-vpc accounts +provider "aws" { + alias = "core-vpc" + region = "eu-west-2" + assume_role { + role_arn = !can(regex("githubactionsrolesession|AdministratorAccess", data.aws_caller_identity.original_session.arn)) ? "arn:aws:iam::${local.environment_management.account_ids[local.provider_name]}:role/member-delegation-read-only" : "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" + assume_role { + role_arn = !can(regex("githubactionsrolesession|AdministratorAccess", data.aws_caller_identity.original_session.arn)) ? "arn:aws:iam::${local.environment_management.account_ids["core-network-services-production"]}:role/read-log-records" : "arn:aws:iam::${local.environment_management.account_ids["core-network-services-production"]}:role/modify-dns-records" + } +} + +# Provider for creating resources in us-east-1, eg ACM resources for CloudFront +provider "aws" { + alias = "us-east-1" + region = "us-east-1" + assume_role { + role_arn = !can(regex("githubactionsrolesession|AdministratorAccess|user", data.aws_caller_identity.original_session.arn)) ? null : can(regex("user", data.aws_caller_identity.original_session.arn)) ? "arn:aws:iam::${local.environment_management.account_ids[terraform.workspace]}:role/${var.collaborator_access}" : "arn:aws:iam::${data.aws_caller_identity.original_session.id}:role/MemberInfrastructureAccessUSEast" + } +} + +# Provider for reading resources from root account IdentityStore +provider "aws" { + region = "eu-west-2" + alias = "sso-readonly" + assume_role { + role_arn = "arn:aws:iam::${local.environment_management.aws_organizations_root_account_id}:role/ModernisationPlatformSSOReadOnly" + } +} diff --git a/terraform/environments/oasys-national-reporting/platform_secrets.tf b/terraform/environments/oasys-national-reporting/platform_secrets.tf new file mode 100644 index 00000000000..bb006856534 --- /dev/null +++ b/terraform/environments/oasys-national-reporting/platform_secrets.tf @@ -0,0 +1,17 @@ +# Get modernisation account id from ssm parameter +data "aws_ssm_parameter" "modernisation_platform_account_id" { + provider = aws.original-session + name = "modernisation_platform_account_id" +} + +# Get secret by arn 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 +} diff --git a/terraform/environments/oasys-national-reporting/platform_versions.tf b/terraform/environments/oasys-national-reporting/platform_versions.tf new file mode 100644 index 00000000000..6161ef3bc02 --- /dev/null +++ b/terraform/environments/oasys-national-reporting/platform_versions.tf @@ -0,0 +1,13 @@ +terraform { + required_providers { + aws = { + version = "~> 5.0" + source = "hashicorp/aws" + } + http = { + version = "~> 3.0" + source = "hashicorp/http" + } + } + required_version = "~> 1.0" +} diff --git a/terraform/environments/oasys-national-reporting/secrets.tf b/terraform/environments/oasys-national-reporting/secrets.tf new file mode 100644 index 00000000000..a6a94d9c098 --- /dev/null +++ b/terraform/environments/oasys-national-reporting/secrets.tf @@ -0,0 +1 @@ +#### This file can be used to store secrets specific to the member account ####