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
This commit is contained in:
Ludovico Magnocavallo
2025-03-29 09:43:27 +01:00
committed by GitHub
parent 0facab6724
commit 554cc47707
12 changed files with 392 additions and 14 deletions

View File

@@ -1 +1,2 @@
**/*-providers.tf **/*-providers.tf
**/*.tfvars

View File

@@ -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
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [atlas_config](variables.tf#L17) | MongoDB Atlas configuration. | <code title="object&#40;&#123;&#10; cluster_name &#61; string&#10; organization_id &#61; string&#10; project_name &#61; string&#10; region &#61; string&#10; database_version &#61; optional&#40;string&#41;&#10; instance_size &#61; optional&#40;string&#41;&#10; provider &#61; object&#40;&#123;&#10; private_key &#61; string&#10; public_key &#61; string&#10; &#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [project_id](variables.tf#L40) | Project id where the registries will be created. | <code>string</code> | ✓ | |
| [vpc_config](variables.tf#L45) | VPC configuration. | <code title="object&#40;&#123;&#10; psc_cidr_block &#61; string&#10; network_name &#61; string&#10; subnetwork_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [name](variables.tf#L33) | Prefix used for all resource names. | <code>string</code> | | <code>&#34;mongodb&#34;</code> |
## 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. | |
<!-- END TFDOC -->
## 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
```

View File

@@ -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
}
}
}

View File

@@ -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
])
}

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
})
}

View File

@@ -17,30 +17,29 @@
# TODO: edit and uncomment the following line to create the project in a folder # TODO: edit and uncomment the following line to create the project in a folder
# parent: shared # parent: shared
name: os-apt-0 name: prod-os-apt-0
services: services:
- accesscontextmanager.googleapis.com - accesscontextmanager.googleapis.com
- artifactregistry.googleapis.com - artifactregistry.googleapis.com
automation: automation:
# TODO: edit the automation project and optionally edit resource names # TODO: edit the automation project and optionally edit resource names
project: pf-automation-0 project: prod-pf-iac-0
service_accounts: service_accounts:
rw: rw:
description: Read/write automation service account for apt registries. description: Read/write automation service account for apt registries.
buckets: bucket:
tf-state: description: Terraform state bucket for apt registries.
description: Terraform state bucket for apt registries. iam:
iam: roles/storage.objectCreator:
roles/storage.objectCreator: - rw
- rw roles/storage.objectViewer:
roles/storage.objectViewer: - rw
- rw
iam: iam:
roles/viewer: roles/viewer:
- rw - prod-os-apt-0/rw
roles/artifactregistry.admin: roles/artifactregistry.admin:
- rw - prod-os-apt-0/rw
# TODO: add instance service accounts that need access to the registries # TODO: add instance service accounts that need access to the registries
# roles/artifactregistry.writer: # roles/artifactregistry.writer:
# - serviceAccount:foo@bar # - serviceAccount:foo@bar

View File

@@ -249,7 +249,8 @@ module "addresses" {
| [ipsec_interconnect_addresses](outputs.tf#L41) | Allocated internal addresses for HA VPN over Cloud Interconnect. | | | [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. | | | [network_attachment_ids](outputs.tf#L49) | IDs of network attachments. | |
| [psa_addresses](outputs.tf#L57) | Allocated internal addresses for PSA endpoints. | | | [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 ## Fixtures

View File

@@ -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" { output "psc_addresses" {
description = "Allocated internal addresses for PSC endpoints." description = "Allocated internal addresses for PSC endpoints."
value = merge( value = merge(

View File

@@ -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 ### Subnet IAM

View File

@@ -23,5 +23,9 @@ terraform {
source = "integrations/github" source = "integrations/github"
version = "~> 5.0" version = "~> 5.0"
} }
mongodbatlas = {
source = "mongodb/mongodbatlas"
version = "~> 1.0"
}
} }
} }