From 554cc47707a2536ba7bdf9a8d36fca21d2c0ac51 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Sat, 29 Mar 2025 09:43:27 +0100 Subject: [PATCH] Mongodb Atlas project template (#2986) * mongodb project definition * wip * add psc output to net-address module * wip * wip * initial README, test * remove providers file * boilerplate * tfdoc * test * fix unrelated test * outputs, better README --- fast/project-templates/.gitignore | 1 + fast/project-templates/data-mongodb/README.md | 90 +++++++++++++++++++ fast/project-templates/data-mongodb/main.tf | 75 ++++++++++++++++ .../project-templates/data-mongodb/outputs.tf | 46 ++++++++++ .../data-mongodb/project.yaml | 47 ++++++++++ .../data-mongodb/providers_override.tf | 29 ++++++ .../data-mongodb/variables.tf | 52 +++++++++++ .../os-apt-registries/project.yaml | 23 +++-- modules/net-address/README.md | 3 +- modules/net-address/outputs.tf | 34 +++++++ modules/net-vpc/README.md | 2 +- tools/lockfile/default-versions_override.tf | 4 + 12 files changed, 392 insertions(+), 14 deletions(-) create mode 100644 fast/project-templates/data-mongodb/README.md create mode 100644 fast/project-templates/data-mongodb/main.tf create mode 100644 fast/project-templates/data-mongodb/outputs.tf create mode 100644 fast/project-templates/data-mongodb/project.yaml create mode 100644 fast/project-templates/data-mongodb/providers_override.tf create mode 100644 fast/project-templates/data-mongodb/variables.tf diff --git a/fast/project-templates/.gitignore b/fast/project-templates/.gitignore index 3073d501b..cc8997e22 100644 --- a/fast/project-templates/.gitignore +++ b/fast/project-templates/.gitignore @@ -1 +1,2 @@ **/*-providers.tf +**/*.tfvars diff --git a/fast/project-templates/data-mongodb/README.md b/fast/project-templates/data-mongodb/README.md new file mode 100644 index 000000000..a44ae55dc --- /dev/null +++ b/fast/project-templates/data-mongodb/README.md @@ -0,0 +1,90 @@ +# MongoDB Atlas + +This simple setup allows creating and configuring a managed [MongoDB Atlas](https://cloud.google.com/mongodb) cluster, and connecting it to a local VPC network via Private Endpoints. + +## Prerequisites + +The [`project.yaml`](./project.yaml) file describes the project-level configuration needed in terms of API activation and IAM bindings. + +If you are deploying this inside a FAST-enabled organization, the file can be lightly edited to match your configuration, and then used directly in the [project factory](../../stages/2-project-factory/). + +This Terraform can of course be deployed using any pre-existing project. In that case use the YAML file to determine the configuration you need to set on the project: + +- enable the APIs listed under `services` +- grant the permissions listed under `iam` to the principal running Terraform, either machine (service account) or human + +## Variable Configuration + +Configuration is mostly done via the `atlas_config` and `vpc_config` variables. Note that: + +- VPC configuration can be set to reference a Shared VPC Host network like shown below, or an in-project network if that is preferred +- the PSC CIDR block is used to allocate the required 50 endpoint addresses in the VPC, so it needs to be large enough to accommodate them +- the Atlas region must match the GCP subnetwork region + +Bringing up a cluster and the associated connectivity from scratch will require approximately 30 minutes. + +```hcl +atlas_config = { + cluster_name = "test-0" + organization_id = "fmoajt0b2fwdvp9yvu7m7zl2" + project_name = "my-atlas-project" + region = "NORTH_AMERICA_NORTHEAST_1" + database_version = "7.0" + instance_size = "M10" + provider = { + public_key = "xxxx" + private_key = "xxxxx-xxxx-xxxx-xxxx-xxxxxxxx" + } +} +project_id = "my-prod-shared-mongodb-0" +vpc_config = { + network_name = "dev-spoke-0" + subnetwork_id = "projects/my-dev-net-spoke-0/regions/northamerica-northeast1/subnetworks/gce" + psc_cidr_block = "10.8.11.192/26" +} +# tftest skip +``` + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [atlas_config](variables.tf#L17) | MongoDB Atlas configuration. | object({…}) | ✓ | | +| [project_id](variables.tf#L40) | Project id where the registries will be created. | string | ✓ | | +| [vpc_config](variables.tf#L45) | VPC configuration. | object({…}) | ✓ | | +| [name](variables.tf#L33) | Prefix used for all resource names. | string | | "mongodb" | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [atlas_cluster](outputs.tf#L17) | MongoDB Atlas cluster. | | +| [atlas_project](outputs.tf#L31) | MongoDB Atlas project. | | +| [endpoints](outputs.tf#L40) | MongoDB Atlas endpoints. | | + +## Test + +```hcl +module "test" { + source = "./fabric/fast/project-templates/data-mongodb" + atlas_config = { + cluster_name = "test-0" + organization_id = "fmoajt0b2fwdvp9yvu7m7zl2" + project_name = "my-atlas-project" + region = "NORTH_AMERICA_NORTHEAST_1" + database_version = "7.0" + instance_size = "M10" + provider = { + public_key = "xxxx" + private_key = "xxxxx-xxxx-xxxx-xxxx-xxxxxxxx" + } + } + project_id = "my-prod-shared-mongodb-0" + vpc_config = { + network_name = "dev-spoke-0" + subnetwork_id = "projects/my-dev-net-spoke-0/regions/northamerica-northeast1/subnetworks/gce" + psc_cidr_block = "10.8.11.192/26" + } +} +# tftest modules=2 resources=104 +``` diff --git a/fast/project-templates/data-mongodb/main.tf b/fast/project-templates/data-mongodb/main.tf new file mode 100644 index 000000000..ed05bb83d --- /dev/null +++ b/fast/project-templates/data-mongodb/main.tf @@ -0,0 +1,75 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +locals { + region = regex( + "projects/[^/]+/regions/([^/]+)/subnetworks/[^/]+$", + var.vpc_config.subnetwork_id + )[0] +} + +module "addresses" { + source = "../../../modules/net-address" + project_id = var.project_id + psc_addresses = { + for i in range(50) : "${var.name}-${i}" => { + address = cidrhost(var.vpc_config.psc_cidr_block, i) + region = local.region + subnet_self_link = var.vpc_config.subnetwork_id + service_attachment = { + psc_service_attachment_link = ( + mongodbatlas_privatelink_endpoint.default.service_attachment_names[i] + ) + global_access = true + } + } + } +} + +resource "mongodbatlas_project" "default" { + name = var.atlas_config.project_name + org_id = var.atlas_config.organization_id +} + +resource "mongodbatlas_cluster" "default" { + project_id = mongodbatlas_project.default.id + name = var.atlas_config.cluster_name + provider_name = "GCP" + provider_instance_size_name = var.atlas_config.instance_size + provider_region_name = var.atlas_config.region + mongo_db_major_version = var.atlas_config.database_version +} + +resource "mongodbatlas_privatelink_endpoint" "default" { + project_id = mongodbatlas_project.default.id + provider_name = "GCP" + region = var.atlas_config.region +} + +resource "mongodbatlas_privatelink_endpoint_service" "default" { + project_id = mongodbatlas_privatelink_endpoint.default.project_id + private_link_id = mongodbatlas_privatelink_endpoint.default.private_link_id + provider_name = "GCP" + endpoint_service_id = var.vpc_config.network_name + gcp_project_id = var.project_id + dynamic "endpoints" { + for_each = module.addresses.psc + content { + ip_address = endpoints.value.address.address + endpoint_name = endpoints.value.forwarding_rule.name + } + } +} diff --git a/fast/project-templates/data-mongodb/outputs.tf b/fast/project-templates/data-mongodb/outputs.tf new file mode 100644 index 000000000..6efd33e78 --- /dev/null +++ b/fast/project-templates/data-mongodb/outputs.tf @@ -0,0 +1,46 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "atlas_cluster" { + description = "MongoDB Atlas cluster." + value = { + id = mongodbatlas_cluster.default.cluster_id + mongo_uri = mongodbatlas_cluster.default.mongo_uri + mongo_uri_with_options = mongodbatlas_cluster.default.mongo_uri_with_options + name = mongodbatlas_cluster.default.name + project_id = mongodbatlas_cluster.default.project_id + region = mongodbatlas_cluster.default.provider_region_name + size = mongodbatlas_cluster.default.provider_instance_size_name + srv_address = mongodbatlas_cluster.default.srv_address + } +} + +output "atlas_project" { + description = "MongoDB Atlas project." + value = { + id = mongodbatlas_project.default.id + name = mongodbatlas_project.default.name + org_id = mongodbatlas_project.default.org_id + } +} + +output "endpoints" { + description = "MongoDB Atlas endpoints." + value = sort([ + for v in mongodbatlas_privatelink_endpoint_service.default.endpoints : + v.ip_address + ]) +} diff --git a/fast/project-templates/data-mongodb/project.yaml b/fast/project-templates/data-mongodb/project.yaml new file mode 100644 index 000000000..3c7a6e073 --- /dev/null +++ b/fast/project-templates/data-mongodb/project.yaml @@ -0,0 +1,47 @@ +# Copyright 2025 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# yaml-language-server: $schema=../../stages/2-project-factory/schemas/project.schema.json + +# edit parent to suit desired folder where project is created +parent: shared +name: prod-shared-mongodb-0 +services: + - compute.googleapis.com + - logging.googleapis.com + - monitoring.googleapis.com +# if automation resources are not used, grant these roles to the principal +# that will be used to apply this Terraform setup +iam: + roles/compute.admin: + - automation/rw + roles/servicedirectory.admin: + - automation/rw +automation: + project: foo-prod-shared-iac-0 + service_accounts: + rw: + description: Read/write automation service account for MongoDB. + bucket: + description: Terraform state bucket for MongoDB. + iam: + roles/storage.objectAdmin: + - automation/rw + roles/storage.objectViewer: + - automation/rw +# edit or comment shared VPC service host +shared_vpc_service_config: + host_project: dev-spoke-0 + network_users: + - automation/rw diff --git a/fast/project-templates/data-mongodb/providers_override.tf b/fast/project-templates/data-mongodb/providers_override.tf new file mode 100644 index 000000000..6d2ad97b6 --- /dev/null +++ b/fast/project-templates/data-mongodb/providers_override.tf @@ -0,0 +1,29 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +terraform { + required_providers { + mongodbatlas = { + source = "mongodb/mongodbatlas" + version = "~> 1.0" + } + } +} + +provider "mongodbatlas" { + public_key = var.atlas_config.provider.public_key + private_key = var.atlas_config.provider.private_key +} diff --git a/fast/project-templates/data-mongodb/variables.tf b/fast/project-templates/data-mongodb/variables.tf new file mode 100644 index 000000000..d2cf44428 --- /dev/null +++ b/fast/project-templates/data-mongodb/variables.tf @@ -0,0 +1,52 @@ +/** + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "atlas_config" { + description = "MongoDB Atlas configuration." + type = object({ + cluster_name = string + organization_id = string + project_name = string + region = string + database_version = optional(string) + instance_size = optional(string) + provider = object({ + private_key = string + public_key = string + }) + }) +} + +variable "name" { + description = "Prefix used for all resource names." + type = string + nullable = true + default = "mongodb" +} + +variable "project_id" { + description = "Project id where the registries will be created." + type = string +} + +variable "vpc_config" { + description = "VPC configuration." + type = object({ + psc_cidr_block = string + network_name = string + subnetwork_id = string + }) +} diff --git a/fast/project-templates/os-apt-registries/project.yaml b/fast/project-templates/os-apt-registries/project.yaml index 8a1e991d8..649fa8a08 100644 --- a/fast/project-templates/os-apt-registries/project.yaml +++ b/fast/project-templates/os-apt-registries/project.yaml @@ -17,30 +17,29 @@ # TODO: edit and uncomment the following line to create the project in a folder # parent: shared -name: os-apt-0 +name: prod-os-apt-0 services: - accesscontextmanager.googleapis.com - artifactregistry.googleapis.com automation: # TODO: edit the automation project and optionally edit resource names - project: pf-automation-0 + project: prod-pf-iac-0 service_accounts: rw: description: Read/write automation service account for apt registries. - buckets: - tf-state: - description: Terraform state bucket for apt registries. - iam: - roles/storage.objectCreator: - - rw - roles/storage.objectViewer: - - rw + bucket: + description: Terraform state bucket for apt registries. + iam: + roles/storage.objectCreator: + - rw + roles/storage.objectViewer: + - rw iam: roles/viewer: - - rw + - prod-os-apt-0/rw roles/artifactregistry.admin: - - rw + - prod-os-apt-0/rw # TODO: add instance service accounts that need access to the registries # roles/artifactregistry.writer: # - serviceAccount:foo@bar \ No newline at end of file diff --git a/modules/net-address/README.md b/modules/net-address/README.md index 677e582f9..29938f777 100644 --- a/modules/net-address/README.md +++ b/modules/net-address/README.md @@ -249,7 +249,8 @@ module "addresses" { | [ipsec_interconnect_addresses](outputs.tf#L41) | Allocated internal addresses for HA VPN over Cloud Interconnect. | | | [network_attachment_ids](outputs.tf#L49) | IDs of network attachments. | | | [psa_addresses](outputs.tf#L57) | Allocated internal addresses for PSA endpoints. | | -| [psc_addresses](outputs.tf#L65) | Allocated internal addresses for PSC endpoints. | | +| [psc](outputs.tf#L65) | Allocated resources for PSC endpoints. | | +| [psc_addresses](outputs.tf#L99) | Allocated internal addresses for PSC endpoints. | | ## Fixtures diff --git a/modules/net-address/outputs.tf b/modules/net-address/outputs.tf index 95ab38b11..5e117ff62 100644 --- a/modules/net-address/outputs.tf +++ b/modules/net-address/outputs.tf @@ -62,6 +62,40 @@ output "psa_addresses" { } } +output "psc" { + description = "Allocated resources for PSC endpoints." + value = merge( + { + for k, v in local.global_psc : + k => { + address = { + address = google_compute_global_address.psc[k].address + id = google_compute_global_address.psc[k].id + name = google_compute_global_address.psc[k].name + } + forwarding_rule = { + id = try(google_compute_global_forwarding_rule.psc_consumer[k].id, null) + name = try(google_compute_global_forwarding_rule.psc_consumer[k].name, null) + } + } + }, + { + for k, v in local.regional_psc : + k => { + address = { + address = google_compute_address.psc[k].address + id = google_compute_address.psc[k].id + name = google_compute_address.psc[k].name + } + forwarding_rule = { + id = try(google_compute_forwarding_rule.psc_consumer[k].id, null) + name = try(google_compute_forwarding_rule.psc_consumer[k].name, null) + } + } + } + ) +} + output "psc_addresses" { description = "Allocated internal addresses for PSC endpoints." value = merge( diff --git a/modules/net-vpc/README.md b/modules/net-vpc/README.md index 8913dea56..62d298d69 100644 --- a/modules/net-vpc/README.md +++ b/modules/net-vpc/README.md @@ -105,7 +105,7 @@ module "vpc" { } ] } -# tftest modules=1 resources=8 inventory=subnet-options.yaml e2e +# tftest modules=1 resources=9 inventory=subnet-options.yaml e2e ``` ### Subnet IAM diff --git a/tools/lockfile/default-versions_override.tf b/tools/lockfile/default-versions_override.tf index 0cf335251..f9822ecdc 100644 --- a/tools/lockfile/default-versions_override.tf +++ b/tools/lockfile/default-versions_override.tf @@ -23,5 +23,9 @@ terraform { source = "integrations/github" version = "~> 5.0" } + mongodbatlas = { + source = "mongodb/mongodbatlas" + version = "~> 1.0" + } } }