diff --git a/blueprints/gcve/pc-minimal/README.md b/blueprints/gcve/pc-minimal/README.md new file mode 100644 index 00000000..a749b787 --- /dev/null +++ b/blueprints/gcve/pc-minimal/README.md @@ -0,0 +1,106 @@ +# GCVE Private Cloud Minimal + +This blueprint presents an opinionated architecture to handle different Google VMware Engine deployment scenarios: from a simple single region private cloud to multi-region private clouds spread across different locations. The general idea behind this blueprint is to deploy a single project hosting one or more GCVE private clouds connected to a shared VMware Engine Network (VEN). +Optionally this blueprint can deploy the VMWare Engine Network peerings to pre-existing VPCs. + +Multiple deployments of this blueprint allow the user to achieve more complex design solutions as for example GCVE private clouds deployed on different projects or connected to indipendent VMWare Engine Networks. + +This blueprint is used as part of the [FAST GCVE stage](../../../fast/stages/3-gcve/) but it can also be used independently if desired. + +

+ GCVE single region private cloud +

+ +The blueprint manages: +- project creation +- project-level organization policy definitions +- billing setup (billing account attachment) +- API/services enablement +- IAM role assignment for groups +- VMware Engine private clouds creation +- [VMware Engine Network](https://cloud.google.com/vmware-engine/docs/networking/vmware-engine-network#standard_networks) creation +- VPC attachment (Optional) + +### User groups + +Based on our GCP best practices, a GCVE private cloud relies on user groups to assign roles to human identities. These are the specific groups bound to the main GCVE [predefined roles](https://cloud.google.com/vmware-engine/docs/iam#vmware-engine-roles): +- *VMware Engine Administrators*. They have full access to the VMWare Engine Service. +- *VMware Engine Viewers*. They have read-only access to the VMware Engine Service. + + +### Network + +This blueprints expects the user to provision a VPC upfront, either from one of the FAST networking stages (e.g. [Networking with separated single environment](../../../fast/stages/2-networking-d-separate-envs)) or from an external source. +The blueprint can optionally configure the [VMware Engine Network peering](https://cloud.google.com/vmware-engine/docs/networking/peer-vpc-network) on the peer VPC by granting the following permissions on the project that hosts the VPC: +- vmwareengine.networkPeerings.create +- vmwareengine.networkPeerings.get +- vmwareengine.networkPeerings.list +- vmwareengine.operations.get +The permissions can be assigned through the predefined role *vmwareengine.vmwareengineAdmin*. Anyway the creation of a dedicated custom roile is strogly recommended to comply with the least privilege principle. + +## Basic usage + +The following example shows how to deploy a CGVE private cloud and connect it to a VPC + +```hcl +module "gcve-pc" { + source = "./fabric/blueprints/gcve/pc-minimal" + billing_account_id = "000000-000000-000000" + folder_id = "folders/000000000000" + project_id = "myprojectid" + groups = { + gcp-gcve-admins = "group:gcp-gcve-admins@acme.com" + gcp-gcve-viewers = "group:gcp-gcve-viewers@acme.com" + } + + prefix = "myprefix" + + network_peerings = { + dev-spoke-ven = { + peer_network = "projects/spokeproject/regions/europe-west1/subnetworks/dev-default-ew1" + peer_project_id = "peerprojectid" + } + } + + private_cloud_configs = { + dev-pc = { + cidr = "172.26.16.0/22" + zone = "europe-west1-a" + management_cluster_config = { + name = "mgmt-cluster" + node_count = 1 + node_type_id = "standard-72" + } + } + } +} +# tftest modules=3 resources=7 +``` + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [gcve-pc.tf](./gcve-pc.tf) | GCVE private cloud. | gcve-private-cloud | google_vmwareengine_network_peering | +| [main.tf](./main.tf) | Project. | project | | +| [output.tf](./output.tf) | Output variables. | | | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [billing_account_id](variables.tf#L17) | Billing account ID. | string | ✓ | | +| [folder_id](variables.tf#L22) | Folder used for the GCVE project in folders/nnnnnnnnnnn format. | string | ✓ | | +| [groups](variables.tf#L27) | GCVE groups. | object({…}) | ✓ | | +| [prefix](variables.tf#L81) | Prefix used for resource names. | string | ✓ | | +| [private_cloud_configs](variables.tf#L90) | The VMware private cloud configurations. The key is the unique private cloud name suffix. | map(object({…})) | ✓ | | +| [project_id](variables.tf#L112) | ID of the project that will contain the GCVE private cloud. | string | ✓ | | +| [iam](variables.tf#L36) | Project-level authoritative IAM bindings for users and service accounts in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | +| [iam_by_principals](variables.tf#L43) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | +| [labels](variables.tf#L50) | Project-level labels. | map(string) | | {} | +| [network_peerings](variables.tf#L56) | The network peerings between users' VPCs and the VMware Engine networks. The key is the peering name suffix. | map(object({…})) | | {} | +| [project_services](variables.tf#L117) | Additional project services to enable. | list(string) | | [] | + diff --git a/blueprints/gcve/pc-minimal/diagram.png b/blueprints/gcve/pc-minimal/diagram.png new file mode 100644 index 00000000..78ae82b2 Binary files /dev/null and b/blueprints/gcve/pc-minimal/diagram.png differ diff --git a/blueprints/gcve/pc-minimal/gcve-pc.tf b/blueprints/gcve/pc-minimal/gcve-pc.tf new file mode 100644 index 00000000..2cbd1e1e --- /dev/null +++ b/blueprints/gcve/pc-minimal/gcve-pc.tf @@ -0,0 +1,58 @@ +/** + * Copyright 2024 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. + */ + +# tfdoc:file:description GCVE private cloud. +locals { + ven_peerings = { + for k, v in var.network_peerings : k => { + peer_network = v.peer_network + description = v.description + export_custom_routes = v.custom_routes.export_to_peer + export_custom_routes_with_public_ip = v.custom_routes_with_public_ip.export_to_peer + import_custom_routes = v.custom_routes.import_from_peer + import_custom_routes_with_public_ip = v.custom_routes_with_public_ip.import_from_peer + peer_to_vmware_engine_network = v.peer_to_vmware_engine_network + } + } +} +module "gcve-pc" { + source = "../../../modules/gcve-private-cloud" + prefix = var.prefix + project_id = module.gcve-project-0.id + + vmw_network_config = { + create = true + name = "default" + } + vmw_network_peerings = local.ven_peerings + + vmw_private_cloud_configs = var.private_cloud_configs +} + +resource "google_vmwareengine_network_peering" "vmw_engine_network_peerings" { + provider = google-beta + for_each = { for k, v in var.network_peerings : k => v if v.configure_peer_network } + peer_network = each.value.peer_network + name = "${var.prefix}-${each.key}" + description = each.value.description + export_custom_routes = each.value.custom_routes.export_to_ven + export_custom_routes_with_public_ip = each.value.custom_routes_with_public_ip.export_to_ven + import_custom_routes = each.value.custom_routes.import_from_ven + import_custom_routes_with_public_ip = each.value.custom_routes_with_public_ip.import_from_ven + peer_network_type = "STANDARD" + project = each.value.peer_project_id + vmware_engine_network = module.gcve-pc.vmw_private_cloud_network.id +} diff --git a/blueprints/gcve/pc-minimal/main.tf b/blueprints/gcve/pc-minimal/main.tf new file mode 100644 index 00000000..2d9f5c8e --- /dev/null +++ b/blueprints/gcve/pc-minimal/main.tf @@ -0,0 +1,39 @@ +/** + * Copyright 2024 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. + */ + +# tfdoc:file:description Project. + +module "gcve-project-0" { + source = "../../../modules/project" + billing_account = var.billing_account_id + name = var.project_id + parent = var.folder_id + prefix = var.prefix + iam_by_principals = merge({ + (var.groups.gcp-gcve-admins) = ["roles/vmwareengine.vmwareengineAdmin"] + (var.groups.gcp-gcve-viewers) = ["roles/vmwareengine.vmwareengineViewer"] + }, + var.iam_by_principals + ) + iam = var.iam + labels = var.labels + services = concat([ + "vmwareengine.googleapis.com", + ], + var.project_services + ) + # specify project-level org policies here if you need them +} diff --git a/blueprints/gcve/pc-minimal/output.tf b/blueprints/gcve/pc-minimal/output.tf new file mode 100644 index 00000000..ba6f2ebf --- /dev/null +++ b/blueprints/gcve/pc-minimal/output.tf @@ -0,0 +1,40 @@ +# Copyright 2024 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 +# +# https://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. + +# tfdoc:file:description Output variables. + +output "project_id" { + description = "GCVE project id." + value = module.gcve-project-0.project_id +} + +output "vmw_engine_network_config" { + description = "VMware engine network configuration." + value = module.gcve-pc.vmw_engine_network_config +} + +output "vmw_engine_network_peerings" { + description = "The peerings created towards the user VPC or other VMware engine networks." + value = module.gcve-pc.vmw_engine_network_peerings +} + +output "vmw_engine_private_clouds" { + description = "VMware engine private cloud resources." + value = module.gcve-pc.vmw_engine_private_clouds +} + +output "vmw_private_cloud_network" { + description = "VMware engine network." + value = module.gcve-pc.vmw_private_cloud_network +} diff --git a/blueprints/gcve/pc-minimal/variables.tf b/blueprints/gcve/pc-minimal/variables.tf new file mode 100644 index 00000000..fa0c99e6 --- /dev/null +++ b/blueprints/gcve/pc-minimal/variables.tf @@ -0,0 +1,122 @@ +/** + * Copyright 2024 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 "billing_account_id" { + description = "Billing account ID." + type = string +} + +variable "folder_id" { + description = "Folder used for the GCVE project in folders/nnnnnnnnnnn format." + type = string +} + +variable "groups" { + description = "GCVE groups." + type = object({ + gcp-gcve-admins = string + gcp-gcve-viewers = string + }) + nullable = false +} + +variable "iam" { + description = "Project-level authoritative IAM bindings for users and service accounts in {ROLE => [MEMBERS]} format." + type = map(list(string)) + default = {} + nullable = false +} + +variable "iam_by_principals" { + description = "Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable." + type = map(list(string)) + default = {} + nullable = false +} + +variable "labels" { + description = "Project-level labels." + type = map(string) + default = {} +} + +variable "network_peerings" { + description = "The network peerings between users' VPCs and the VMware Engine networks. The key is the peering name suffix." + type = map(object({ + peer_network = string + configure_peer_network = optional(bool, false) + custom_routes = optional(object({ + export_to_peer = optional(bool, false) + import_from_peer = optional(bool, false) + export_to_ven = optional(bool, false) + import_from_ven = optional(bool, false) + }), {}) + custom_routes_with_public_ip = optional(object({ + export_to_peer = optional(bool, false) + import_from_peer = optional(bool, false) + export_to_ven = optional(bool, false) + import_from_ven = optional(bool, false) + }), {}) + description = optional(string, "Managed by Terraform.") + peer_project_id = optional(string) + peer_to_vmware_engine_network = optional(bool, false) + })) + nullable = false + default = {} +} + +variable "prefix" { + description = "Prefix used for resource names." + type = string + validation { + condition = var.prefix != "" + error_message = "Prefix cannot be empty." + } +} + +variable "private_cloud_configs" { + description = "The VMware private cloud configurations. The key is the unique private cloud name suffix." + type = map(object({ + cidr = string + zone = string + # The key is the unique additional cluster name suffix + additional_cluster_configs = optional(map(object({ + custom_core_count = optional(number) + node_count = optional(number, 3) + node_type_id = optional(string, "standard-72") + })), {}) + management_cluster_config = optional(object({ + custom_core_count = optional(number) + name = optional(string, "mgmt-cluster") + node_count = optional(number, 3) + node_type_id = optional(string, "standard-72") + }), {}) + description = optional(string, "Managed by Terraform.") + })) + nullable = false +} + +variable "project_id" { + description = "ID of the project that will contain the GCVE private cloud." + type = string +} + +variable "project_services" { + description = "Additional project services to enable." + type = list(string) + default = [] + nullable = false +} diff --git a/fast/stages/0-bootstrap/README.md b/fast/stages/0-bootstrap/README.md index 1e80e285..3b3d6ce6 100644 --- a/fast/stages/0-bootstrap/README.md +++ b/fast/stages/0-bootstrap/README.md @@ -626,26 +626,26 @@ The `fast_features` variable consists of 4 toggles: | name | description | type | required | default | producer | |---|---|:---:|:---:|:---:|:---:| | [billing_account](variables.tf#L17) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | | -| [organization](variables.tf#L229) | Organization details. | object({…}) | ✓ | | | -| [prefix](variables.tf#L244) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | +| [organization](variables.tf#L230) | Organization details. | object({…}) | ✓ | | | +| [prefix](variables.tf#L245) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | | | [bootstrap_user](variables.tf#L27) | Email of the nominal user running this stage for the first time. | string | | null | | | [cicd_repositories](variables.tf#L33) | CI/CD repository configuration. Identity providers reference keys in the `federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | | [custom_roles](variables.tf#L79) | Map of role names => list of permissions to additionally create at the organization level. | map(list(string)) | | {} | | | [essential_contacts](variables.tf#L86) | Email used for essential contacts, unset if null. | string | | null | | | [factories_config](variables.tf#L92) | Configuration for the resource factories or external data. | object({…}) | | {} | | -| [fast_features](variables.tf#L104) | Selective control for top-level FAST features. | object({…}) | | {} | | -| [group_iam](variables.tf#L117) | Organization-level authoritative IAM binding for groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | | -| [groups](variables.tf#L124) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | -| [iam](variables.tf#L140) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | -| [iam_bindings_additive](variables.tf#L147) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | -| [iam_by_principals](variables.tf#L162) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | -| [locations](variables.tf#L169) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | -| [log_sinks](variables.tf#L183) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | -| [org_policies_config](variables.tf#L212) | Organization policies customization. | object({…}) | | {} | | -| [outputs_location](variables.tf#L238) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [project_parent_ids](variables.tf#L253) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | -| [workforce_identity_providers](variables.tf#L264) | Workforce Identity Federation pools. | map(object({…})) | | {} | | -| [workload_identity_providers](variables.tf#L280) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | +| [fast_features](variables.tf#L104) | Selective control for top-level FAST features. | object({…}) | | {} | | +| [group_iam](variables.tf#L118) | Organization-level authoritative IAM binding for groups, in {GROUP_EMAIL => [ROLES]} format. Group emails need to be static. Can be used in combination with the `iam` variable. | map(list(string)) | | {} | | +| [groups](variables.tf#L125) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | | +| [iam](variables.tf#L141) | Organization-level custom IAM settings in role => [principal] format. | map(list(string)) | | {} | | +| [iam_bindings_additive](variables.tf#L148) | Organization-level custom additive IAM bindings. Keys are arbitrary. | map(object({…})) | | {} | | +| [iam_by_principals](variables.tf#L163) | Authoritative IAM binding in {PRINCIPAL => [ROLES]} format. Principals need to be statically defined to avoid cycle errors. Merged internally with the `iam` variable. | map(list(string)) | | {} | | +| [locations](variables.tf#L170) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {} | | +| [log_sinks](variables.tf#L184) | Org-level log sinks, in name => {type, filter} format. | map(object({…})) | | {…} | | +| [org_policies_config](variables.tf#L213) | Organization policies customization. | object({…}) | | {} | | +| [outputs_location](variables.tf#L239) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [project_parent_ids](variables.tf#L254) | Optional parents for projects created here in folders/nnnnnnn format. Null values will use the organization as parent. | object({…}) | | {} | | +| [workforce_identity_providers](variables.tf#L265) | Workforce Identity Federation pools. | map(object({…})) | | {} | | +| [workload_identity_providers](variables.tf#L281) | Workload Identity Federation pools. The `cicd_repositories` variable references keys here. | map(object({…})) | | {} | | ## Outputs diff --git a/fast/stages/0-bootstrap/data/custom-roles/gcve_network_admin.yaml b/fast/stages/0-bootstrap/data/custom-roles/gcve_network_admin.yaml new file mode 100644 index 00000000..255ac781 --- /dev/null +++ b/fast/stages/0-bootstrap/data/custom-roles/gcve_network_admin.yaml @@ -0,0 +1,21 @@ +# Copyright 2023 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. + +name: gcveNetworkAdmin +includedPermissions: + - vmwareengine.networkPeerings.create + - vmwareengine.networkPeerings.delete + - vmwareengine.networkPeerings.get + - vmwareengine.networkPeerings.list + - vmwareengine.operations.get diff --git a/fast/stages/0-bootstrap/variables.tf b/fast/stages/0-bootstrap/variables.tf index d8536fc2..5493f5d6 100644 --- a/fast/stages/0-bootstrap/variables.tf +++ b/fast/stages/0-bootstrap/variables.tf @@ -105,6 +105,7 @@ variable "fast_features" { description = "Selective control for top-level FAST features." type = object({ data_platform = optional(bool, false) + gcve = optional(bool, false) gke = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) diff --git a/fast/stages/1-resman/README.md b/fast/stages/1-resman/README.md index a84c704f..628280ac 100644 --- a/fast/stages/1-resman/README.md +++ b/fast/stages/1-resman/README.md @@ -327,6 +327,7 @@ Due to its simplicity, this stage lends itself easily to customizations: adding |---|---|---|---| | [billing.tf](./billing.tf) | Billing resources for external billing use cases. | | google_billing_account_iam_member | | [branch-data-platform.tf](./branch-data-platform.tf) | Data Platform stages resources. | folder · gcs · iam-service-account | | +| [branch-gcve.tf](./branch-gcve.tf) | GCVE stage resources. | folder · gcs · iam-service-account | | | [branch-gke.tf](./branch-gke.tf) | GKE multitenant stage resources. | folder · gcs · iam-service-account | | | [branch-networking.tf](./branch-networking.tf) | Networking stage resources. | folder · gcs · iam-service-account | | | [branch-project-factory.tf](./branch-project-factory.tf) | Project factory stage resources. | gcs · iam-service-account | | @@ -336,6 +337,7 @@ Due to its simplicity, this stage lends itself easily to customizations: adding | [branch-tenants.tf](./branch-tenants.tf) | Lightweight tenant resources. | folder · gcs · iam-service-account · project | | | [checklist.tf](./checklist.tf) | None | folder | | | [cicd-data-platform.tf](./cicd-data-platform.tf) | CI/CD resources for the data platform branch. | iam-service-account · source-repository | | +| [cicd-gcve.tf](./cicd-gcve.tf) | CI/CD resources for the GCVE branch. | iam-service-account · source-repository | | | [cicd-gke.tf](./cicd-gke.tf) | CI/CD resources for the GKE multitenant branch. | iam-service-account · source-repository | | | [cicd-networking.tf](./cicd-networking.tf) | CI/CD resources for the networking branch. | iam-service-account · source-repository | | | [cicd-project-factory.tf](./cicd-project-factory.tf) | CI/CD resources for the teams branch. | iam-service-account · source-repository | | @@ -356,35 +358,36 @@ Due to its simplicity, this stage lends itself easily to customizations: adding |---|---|:---:|:---:|:---:|:---:| | [automation](variables.tf#L20) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L42) | Billing account id. If billing account is not part of the same org set `is_org_level` to `false`. To disable handling of billing IAM roles set `no_iam` to `true`. | object({…}) | ✓ | | 0-bootstrap | -| [organization](variables.tf#L213) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L229) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | -| [cicd_repositories](variables.tf#L53) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | -| [custom_roles](variables.tf#L135) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | -| [factories_config](variables.tf#L145) | Configuration for the resource factories or external data. | object({…}) | | {} | | -| [fast_features](variables.tf#L154) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | -| [groups](variables.tf#L168) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | -| [locations](variables.tf#L183) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {…} | 0-bootstrap | -| [org_policy_tags](variables.tf#L201) | Resource management tags for organization policy exceptions. | object({…}) | | {} | 0-bootstrap | -| [outputs_location](variables.tf#L223) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | -| [tag_names](variables.tf#L240) | Customized names for resource management tags. | object({…}) | | {} | | -| [tags](variables.tf#L255) | Custome secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | -| [team_folders](variables.tf#L276) | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | -| [tenants](variables.tf#L292) | Lightweight tenant definitions. | map(object({…})) | | {} | | -| [tenants_config](variables.tf#L308) | Lightweight tenants shared configuration. Roles will be assigned to tenant admin group and service accounts. | object({…}) | | {} | | +| [organization](variables.tf#L227) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L243) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [cicd_repositories](variables.tf#L53) | CI/CD repository configuration. Identity providers reference keys in the `automation.federated_identity_providers` variable. Set to null to disable, or set individual repositories to null if not needed. | object({…}) | | null | | +| [custom_roles](variables.tf#L147) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | +| [factories_config](variables.tf#L158) | Configuration for the resource factories or external data. | object({…}) | | {} | | +| [fast_features](variables.tf#L167) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | +| [groups](variables.tf#L182) | Group names or IAM-format principals to grant organization-level permissions. If just the name is provided, the 'group:' principal and organization domain are interpolated. | object({…}) | | {} | 0-bootstrap | +| [locations](variables.tf#L197) | Optional locations for GCS, BigQuery, and logging buckets created here. | object({…}) | | {…} | 0-bootstrap | +| [org_policy_tags](variables.tf#L215) | Resource management tags for organization policy exceptions. | object({…}) | | {} | 0-bootstrap | +| [outputs_location](variables.tf#L237) | Enable writing provider, tfvars and CI/CD workflow files to local filesystem. Leave null to disable. | string | | null | | +| [tag_names](variables.tf#L254) | Customized names for resource management tags. | object({…}) | | {} | | +| [tags](variables.tf#L269) | Custome secure tags by key name. The `iam` attribute behaves like the similarly named one at module level. | map(object({…})) | | {} | | +| [team_folders](variables.tf#L290) | Team folders to be created. Format is described in a code comment. | map(object({…})) | | null | | +| [tenants](variables.tf#L306) | Lightweight tenant definitions. | map(object({…})) | | {} | | +| [tenants_config](variables.tf#L322) | Lightweight tenants shared configuration. Roles will be assigned to tenant admin group and service accounts. | object({…}) | | {} | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [cicd_repositories](outputs.tf#L337) | WIF configuration for CI/CD repositories. | | | -| [dataplatform](outputs.tf#L351) | Data for the Data Platform stage. | | | -| [gke_multitenant](outputs.tf#L367) | Data for the GKE multitenant stage. | | 03-gke-multitenant | -| [networking](outputs.tf#L388) | Data for the networking stage. | | | -| [project_factories](outputs.tf#L397) | Data for the project factories stage. | | | -| [providers](outputs.tf#L412) | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · 03-dataplatform · xx-sandbox · xx-teams | -| [sandbox](outputs.tf#L419) | Data for the sandbox stage. | | xx-sandbox | -| [security](outputs.tf#L433) | Data for the networking stage. | | 02-security | -| [team_cicd_repositories](outputs.tf#L443) | WIF configuration for Team CI/CD repositories. | | | -| [teams](outputs.tf#L457) | Data for the teams stage. | | | -| [tfvars](outputs.tf#L469) | Terraform variable files for the following stages. | ✓ | | +| [cicd_repositories](outputs.tf#L391) | WIF configuration for CI/CD repositories. | | | +| [dataplatform](outputs.tf#L405) | Data for the Data Platform stage. | | | +| [gcve](outputs.tf#L421) | Data for the GCVE stage. | | 03-gke-multitenant | +| [gke_multitenant](outputs.tf#L442) | Data for the GKE multitenant stage. | | 03-gke-multitenant | +| [networking](outputs.tf#L463) | Data for the networking stage. | | | +| [project_factories](outputs.tf#L472) | Data for the project factories stage. | | | +| [providers](outputs.tf#L487) | Terraform provider files for this stage and dependent stages. | ✓ | 02-networking · 02-security · 03-dataplatform · xx-sandbox · xx-teams | +| [sandbox](outputs.tf#L494) | Data for the sandbox stage. | | xx-sandbox | +| [security](outputs.tf#L508) | Data for the networking stage. | | 02-security | +| [team_cicd_repositories](outputs.tf#L518) | WIF configuration for Team CI/CD repositories. | | | +| [teams](outputs.tf#L532) | Data for the teams stage. | | | +| [tfvars](outputs.tf#L544) | Terraform variable files for the following stages. | ✓ | | diff --git a/fast/stages/1-resman/billing.tf b/fast/stages/1-resman/billing.tf index c1870842..64b87c26 100644 --- a/fast/stages/1-resman/billing.tf +++ b/fast/stages/1-resman/billing.tf @@ -27,6 +27,8 @@ locals { local.branch_optional_sa_lists.dp-prod, local.branch_optional_sa_lists.gke-dev, local.branch_optional_sa_lists.gke-prod, + local.branch_optional_sa_lists.gcve-dev, + local.branch_optional_sa_lists.gcve-prod, local.branch_optional_sa_lists.pf-dev, local.branch_optional_sa_lists.pf-prod, ) diff --git a/fast/stages/1-resman/branch-gcve.tf b/fast/stages/1-resman/branch-gcve.tf new file mode 100644 index 00000000..43b73762 --- /dev/null +++ b/fast/stages/1-resman/branch-gcve.tf @@ -0,0 +1,199 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description GCVE stage resources. + +module "branch-gcve-folder" { + source = "../../../modules/folder" + count = var.fast_features.gcve ? 1 : 0 + parent = "organizations/${var.organization.id}" + name = "GCVE" + tag_bindings = { + context = try( + module.organization.tag_values["${var.tag_names.context}/gcve"].id, null + ) + } +} + +module "branch-gcve-dev-folder" { + source = "../../../modules/folder" + count = var.fast_features.gcve ? 1 : 0 + parent = module.branch-gcve-folder.0.id + name = "Development" + iam = { + # read-write (apply) automation service account + "roles/owner" = [module.branch-gcve-dev-sa.0.iam_email] + "roles/logging.admin" = [module.branch-gcve-dev-sa.0.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-gcve-dev-sa.0.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-gcve-dev-sa.0.iam_email] + "roles/compute.xpnAdmin" = [module.branch-gcve-dev-sa.0.iam_email] + # read-only (plan) automation service account + "roles/viewer" = [module.branch-gcve-dev-r-sa.0.iam_email] + "roles/resourcemanager.folderViewer" = [module.branch-gcve-dev-r-sa.0.iam_email] + } + tag_bindings = { + context = try( + module.organization.tag_values["${var.tag_names.environment}/development"].id, + null + ) + } +} + +module "branch-gcve-prod-folder" { + source = "../../../modules/folder" + count = var.fast_features.gcve ? 1 : 0 + parent = module.branch-gcve-folder.0.id + name = "Production" + iam = { + # read-write (apply) automation service account + "roles/owner" = [module.branch-gcve-prod-sa.0.iam_email] + "roles/logging.admin" = [module.branch-gcve-prod-sa.0.iam_email] + "roles/resourcemanager.folderAdmin" = [module.branch-gcve-prod-sa.0.iam_email] + "roles/resourcemanager.projectCreator" = [module.branch-gcve-prod-sa.0.iam_email] + "roles/compute.xpnAdmin" = [module.branch-gcve-prod-sa.0.iam_email] + # read-only (plan) automation service account + "roles/viewer" = [module.branch-gcve-prod-r-sa.0.iam_email] + "roles/resourcemanager.folderViewer" = [module.branch-gcve-prod-r-sa.0.iam_email] + } + tag_bindings = { + context = try( + module.organization.tag_values["${var.tag_names.environment}/production"].id, + null + ) + } +} + +# automation service accounts + +module "branch-gcve-dev-sa" { + source = "../../../modules/iam-service-account" + count = var.fast_features.gcve ? 1 : 0 + project_id = var.automation.project_id + name = "dev-resman-gcve-0" + display_name = "Terraform GCVE development service account." + prefix = var.prefix + iam = { + "roles/iam.serviceAccountTokenCreator" = concat( + [local.principals.gcp-devops], + compact([ + try(module.branch-gcve-dev-sa-cicd.0.iam_email, null) + ]) + ) + } + iam_project_roles = { + (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] + } + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.objectAdmin"] + } +} + +module "branch-gcve-prod-sa" { + source = "../../../modules/iam-service-account" + count = var.fast_features.gcve ? 1 : 0 + project_id = var.automation.project_id + name = "prod-resman-gcve-0" + display_name = "Terraform GCVE production service account." + prefix = var.prefix + iam = { + "roles/iam.serviceAccountTokenCreator" = concat( + [local.principals.gcp-devops], + compact([ + try(module.branch-gcve-prod-sa-cicd.0.iam_email, null) + ]) + ) + } + iam_project_roles = { + (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] + } + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.objectAdmin"] + } +} + +# automation read-only service accounts + +module "branch-gcve-dev-r-sa" { + source = "../../../modules/iam-service-account" + count = var.fast_features.gcve ? 1 : 0 + project_id = var.automation.project_id + name = "dev-resman-gcve-0r" + display_name = "Terraform GCVE development service account (read-only)." + prefix = var.prefix + iam = { + "roles/iam.serviceAccountTokenCreator" = compact([ + try(module.branch-gcve-dev-r-sa-cicd.0.iam_email, null) + ]) + } + iam_project_roles = { + (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] + } + iam_storage_roles = { + (var.automation.outputs_bucket) = [var.custom_roles["storage_viewer"]] + } +} + +module "branch-gcve-prod-r-sa" { + source = "../../../modules/iam-service-account" + count = var.fast_features.gcve ? 1 : 0 + project_id = var.automation.project_id + name = "prod-resman-gcve-0r" + display_name = "Terraform GCVE production service account (read-only)." + prefix = var.prefix + iam = { + "roles/iam.serviceAccountTokenCreator" = compact([ + try(module.branch-gcve-prod-r-sa-cicd.0.iam_email, null) + ]) + } + iam_project_roles = { + (var.automation.project_id) = ["roles/serviceusage.serviceUsageConsumer"] + } + iam_storage_roles = { + (var.automation.outputs_bucket) = [var.custom_roles["storage_viewer"]] + } +} + +# automation buckets + +module "branch-gcve-dev-gcs" { + source = "../../../modules/gcs" + count = var.fast_features.gcve ? 1 : 0 + project_id = var.automation.project_id + name = "dev-resman-gcve-0" + prefix = var.prefix + location = var.locations.gcs + storage_class = local.gcs_storage_class + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-gcve-dev-sa.0.iam_email] + "roles/storage.objectViewer" = [module.branch-gcve-dev-r-sa.0.iam_email] + } +} + +module "branch-gcve-prod-gcs" { + source = "../../../modules/gcs" + count = var.fast_features.gcve ? 1 : 0 + project_id = var.automation.project_id + name = "prod-resman-gcve-0" + prefix = var.prefix + location = var.locations.gcs + storage_class = local.gcs_storage_class + versioning = true + iam = { + "roles/storage.objectAdmin" = [module.branch-gcve-prod-sa.0.iam_email] + "roles/storage.objectViewer" = [module.branch-gcve-prod-r-sa.0.iam_email] + } +} diff --git a/fast/stages/1-resman/branch-networking.tf b/fast/stages/1-resman/branch-networking.tf index 48bb88dc..5bb641c6 100644 --- a/fast/stages/1-resman/branch-networking.tf +++ b/fast/stages/1-resman/branch-networking.tf @@ -54,14 +54,17 @@ module "branch-network-prod-folder" { (local.custom_roles.service_project_network_admin) = concat( local.branch_optional_sa_lists.dp-prod, local.branch_optional_sa_lists.gke-prod, + local.branch_optional_sa_lists.gcve-prod, local.branch_optional_sa_lists.pf-prod, ) # read-only (plan) automation service accounts "roles/compute.networkViewer" = concat( local.branch_optional_r_sa_lists.dp-prod, local.branch_optional_r_sa_lists.gke-prod, + local.branch_optional_r_sa_lists.gcve-prod, local.branch_optional_r_sa_lists.pf-prod, ) + (local.custom_roles.gcve_network_admin) = local.branch_optional_sa_lists.gcve-prod } tag_bindings = { environment = try( @@ -80,14 +83,17 @@ module "branch-network-dev-folder" { (local.custom_roles.service_project_network_admin) = concat( local.branch_optional_sa_lists.dp-dev, local.branch_optional_sa_lists.gke-dev, + local.branch_optional_sa_lists.gcve-dev, local.branch_optional_sa_lists.pf-dev, ) # read-only (plan) automation service accounts "roles/compute.networkViewer" = concat( local.branch_optional_r_sa_lists.dp-prod, local.branch_optional_r_sa_lists.gke-prod, + local.branch_optional_r_sa_lists.gcve-dev, local.branch_optional_r_sa_lists.pf-prod, ) + (local.custom_roles.gcve_network_admin) = local.branch_optional_sa_lists.gcve-dev } tag_bindings = { environment = try( diff --git a/fast/stages/1-resman/cicd-gcve.tf b/fast/stages/1-resman/cicd-gcve.tf new file mode 100644 index 00000000..7cadd653 --- /dev/null +++ b/fast/stages/1-resman/cicd-gcve.tf @@ -0,0 +1,245 @@ +/** + * Copyright 2022 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. + */ + +# tfdoc:file:description CI/CD resources for the GCVE branch. + +# source repositories + +module "branch-gcve-dev-cicd-repo" { + source = "../../../modules/source-repository" + for_each = ( + try(local.cicd_repositories.gcve_dev.type, null) == "sourcerepo" + ? { 0 = local.cicd_repositories.gcve_dev } + : {} + ) + project_id = var.automation.project_id + name = each.value.name + iam = { + "roles/source.admin" = compact([ + try(module.branch-gcve-dev-sa.0.iam_email, "") + ]) + "roles/source.reader" = compact([ + try(module.branch-gcve-dev-sa-cicd.0.iam_email, "") + ]) + } + triggers = { + fast-03-gcve-dev = { + filename = ".cloudbuild/workflow.yaml" + included_files = [ + "**/*json", "**/*tf", "**/*yaml", ".cloudbuild/workflow.yaml" + ] + service_account = module.branch-gcve-dev-sa-cicd.0.id + substitutions = {} + template = { + project_id = null + branch_name = each.value.branch + repo_name = each.value.name + tag_name = null + } + } + } + depends_on = [module.branch-gcve-dev-sa-cicd] +} + +module "branch-gcve-prod-cicd-repo" { + source = "../../../modules/source-repository" + for_each = ( + try(local.cicd_repositories.gcve_prod.type, null) == "sourcerepo" + ? { 0 = local.cicd_repositories.gcve_prod } + : {} + ) + project_id = var.automation.project_id + name = each.value.name + iam = { + "roles/source.admin" = [module.branch-gcve-prod-sa.0.iam_email] + "roles/source.reader" = [module.branch-gcve-prod-sa-cicd.0.iam_email] + } + triggers = { + fast-03-gcve-prod = { + filename = ".cloudbuild/workflow.yaml" + included_files = [ + "**/*json", "**/*tf", "**/*yaml", ".cloudbuild/workflow.yaml" + ] + service_account = module.branch-gcve-prod-sa-cicd.0.id + substitutions = {} + template = { + project_id = null + branch_name = each.value.branch + repo_name = each.value.name + tag_name = null + } + } + } + depends_on = [module.branch-gcve-prod-sa-cicd] +} + +# read-write (apply) SAs used by CI/CD workflows to impersonate automation SAs + +module "branch-gcve-dev-sa-cicd" { + source = "../../../modules/iam-service-account" + for_each = ( + try(local.cicd_repositories.gcve_dev.name, null) != null + ? { 0 = local.cicd_repositories.gcve_dev } + : {} + ) + project_id = var.automation.project_id + name = "dev-resman-gcve-1" + display_name = "Terraform CI/CD GCVE development service account." + prefix = var.prefix + iam = ( + each.value.type == "sourcerepo" + # used directly from the cloud build trigger for source repos + ? { + "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam + } + # impersonated via workload identity federation for external repos + : { + "roles/iam.workloadIdentityUser" = [ + each.value.branch == null + ? format( + local.identity_providers[each.value.identity_provider].principal_repo, + var.automation.federated_identity_pool, + each.value.name + ) + : format( + local.identity_providers[each.value.identity_provider].principal_branch, + var.automation.federated_identity_pool, + each.value.name, + each.value.branch + ) + ] + } + ) + iam_project_roles = { + (var.automation.project_id) = ["roles/logging.logWriter"] + } + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.objectViewer"] + } +} + +module "branch-gcve-prod-sa-cicd" { + source = "../../../modules/iam-service-account" + for_each = ( + try(local.cicd_repositories.gcve_prod.name, null) != null + ? { 0 = local.cicd_repositories.gcve_prod } + : {} + ) + project_id = var.automation.project_id + name = "prod-resman-gcve-1" + display_name = "Terraform CI/CD GCVE production service account." + prefix = var.prefix + iam = ( + each.value.type == "sourcerepo" + # used directly from the cloud build trigger for source repos + ? { + "roles/iam.serviceAccountUser" = local.automation_resman_sa_iam + } + # impersonated via workload identity federation for external repos + : { + "roles/iam.workloadIdentityUser" = [ + each.value.branch == null + ? format( + local.identity_providers[each.value.identity_provider].principal_repo, + var.automation.federated_identity_pool, + each.value.name + ) + : format( + local.identity_providers[each.value.identity_provider].principal_branch, + var.automation.federated_identity_pool, + each.value.name, + each.value.branch + ) + ] + } + ) + iam_project_roles = { + (var.automation.project_id) = ["roles/logging.logWriter"] + } + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.objectViewer"] + } +} + +# read-only (plan) SAs used by CI/CD workflows to impersonate automation SAs + +module "branch-gcve-dev-r-sa-cicd" { + source = "../../../modules/iam-service-account" + for_each = ( + try(local.cicd_repositories.gcve_dev.name, null) != null + ? { 0 = local.cicd_repositories.gcve_dev } + : {} + ) + project_id = var.automation.project_id + name = "dev-resman-gcve-1r" + display_name = "Terraform CI/CD GCVE development service account (read-only)." + prefix = var.prefix + iam = ( + each.value.type == "sourcerepo" + # build trigger for read-only SA is optionally defined by users + ? {} + # impersonated via workload identity federation for external repos + : { + "roles/iam.workloadIdentityUser" = [ + format( + local.identity_providers[each.value.identity_provider].principal_repo, + var.automation.federated_identity_pool, + each.value.name + ) + ] + } + ) + iam_project_roles = { + (var.automation.project_id) = ["roles/logging.logWriter"] + } + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.objectViewer"] + } +} + +module "branch-gcve-prod-r-sa-cicd" { + source = "../../../modules/iam-service-account" + for_each = ( + try(local.cicd_repositories.gcve_prod.name, null) != null + ? { 0 = local.cicd_repositories.gcve_prod } + : {} + ) + project_id = var.automation.project_id + name = "prod-resman-gcve-1r" + display_name = "Terraform CI/CD GCVE production service account (read-only)." + prefix = var.prefix + iam = ( + each.value.type == "sourcerepo" + # build trigger for read-only SA is optionally defined by users + ? {} + # impersonated via workload identity federation for external repos + : { + "roles/iam.workloadIdentityUser" = [ + format( + local.identity_providers[each.value.identity_provider].principal_repo, + var.automation.federated_identity_pool, + each.value.name + ) + ] + } + ) + iam_project_roles = { + (var.automation.project_id) = ["roles/logging.logWriter"] + } + iam_storage_roles = { + (var.automation.outputs_bucket) = ["roles/storage.objectViewer"] + } +} diff --git a/fast/stages/1-resman/main.tf b/fast/stages/1-resman/main.tf index 9d85a33f..19d2e8c5 100644 --- a/fast/stages/1-resman/main.tf +++ b/fast/stages/1-resman/main.tf @@ -26,20 +26,24 @@ locals { ) # service accounts that receive additional grants on networking/security branch_optional_sa_lists = { - dp-dev = compact([try(module.branch-dp-dev-sa.0.iam_email, "")]) - dp-prod = compact([try(module.branch-dp-prod-sa.0.iam_email, "")]) - gke-dev = compact([try(module.branch-gke-dev-sa.0.iam_email, "")]) - gke-prod = compact([try(module.branch-gke-prod-sa.0.iam_email, "")]) - pf-dev = compact([try(module.branch-pf-dev-sa.0.iam_email, "")]) - pf-prod = compact([try(module.branch-pf-prod-sa.0.iam_email, "")]) + dp-dev = compact([try(module.branch-dp-dev-sa.0.iam_email, "")]) + dp-prod = compact([try(module.branch-dp-prod-sa.0.iam_email, "")]) + gcve-dev = compact([try(module.branch-gcve-dev-sa.0.iam_email, "")]) + gcve-prod = compact([try(module.branch-gcve-prod-sa.0.iam_email, "")]) + gke-dev = compact([try(module.branch-gke-dev-sa.0.iam_email, "")]) + gke-prod = compact([try(module.branch-gke-prod-sa.0.iam_email, "")]) + pf-dev = compact([try(module.branch-pf-dev-sa.0.iam_email, "")]) + pf-prod = compact([try(module.branch-pf-prod-sa.0.iam_email, "")]) } branch_optional_r_sa_lists = { - dp-dev = compact([try(module.branch-dp-dev-r-sa.0.iam_email, "")]) - dp-prod = compact([try(module.branch-dp-prod-r-sa.0.iam_email, "")]) - gke-dev = compact([try(module.branch-gke-dev-r-sa.0.iam_email, "")]) - gke-prod = compact([try(module.branch-gke-prod-r-sa.0.iam_email, "")]) - pf-dev = compact([try(module.branch-pf-dev-r-sa.0.iam_email, "")]) - pf-prod = compact([try(module.branch-pf-prod-r-sa.0.iam_email, "")]) + dp-dev = compact([try(module.branch-dp-dev-r-sa.0.iam_email, "")]) + dp-prod = compact([try(module.branch-dp-prod-r-sa.0.iam_email, "")]) + gcve-dev = compact([try(module.branch-gcve-dev-r-sa.0.iam_email, "")]) + gcve-prod = compact([try(module.branch-gcve-prod-r-sa.0.iam_email, "")]) + gke-dev = compact([try(module.branch-gke-dev-r-sa.0.iam_email, "")]) + gke-prod = compact([try(module.branch-gke-prod-r-sa.0.iam_email, "")]) + pf-dev = compact([try(module.branch-pf-dev-r-sa.0.iam_email, "")]) + pf-prod = compact([try(module.branch-pf-prod-r-sa.0.iam_email, "")]) } # normalize CI/CD repositories cicd_repositories = { diff --git a/fast/stages/1-resman/organization.tf b/fast/stages/1-resman/organization.tf index 90ded30e..6c380c89 100644 --- a/fast/stages/1-resman/organization.tf +++ b/fast/stages/1-resman/organization.tf @@ -50,6 +50,7 @@ module "organization" { values = { data = {} gke = {} + gcve = {} networking = {} sandbox = {} security = {} diff --git a/fast/stages/1-resman/outputs.tf b/fast/stages/1-resman/outputs.tf index 8d229e68..38aa35c4 100644 --- a/fast/stages/1-resman/outputs.tf +++ b/fast/stages/1-resman/outputs.tf @@ -39,6 +39,28 @@ locals { } tf_var_files = local.cicd_workflow_var_files.stage_3 } + gcve_dev = { + service_accounts = { + apply = try(module.branch-gcve-dev-sa-cicd.0.email, null) + plan = try(module.branch-gcve-dev-r-sa-cicd.0.email, null) + } + tf_providers_files = { + apply = "3-gcve-dev-providers.tf" + plan = "3-gcve-dev-r-providers.tf" + } + tf_var_files = local.cicd_workflow_var_files.stage_3 + } + gcve_prod = { + service_accounts = { + apply = try(module.branch-gcve-prod-sa-cicd.0.email, null) + plan = try(module.branch-gcve-prod-r-sa-cicd.0.email, null) + } + tf_providers_files = { + apply = "3-gcve-prod-providers.tf" + plan = "3-gcve-prod-r-providers.tf" + } + tf_var_files = local.cicd_workflow_var_files.stage_3 + } gke_dev = { service_accounts = { apply = try(module.branch-gke-dev-sa-cicd.0.email, null) @@ -125,6 +147,8 @@ locals { { data-platform-dev = try(module.branch-dp-dev-folder.0.id, null) data-platform-prod = try(module.branch-dp-prod-folder.0.id, null) + gcve-dev = try(module.branch-gcve-dev-folder.0.id, null) + gcve-prod = try(module.branch-gcve-prod-folder.0.id, null) gke-dev = try(module.branch-gke-dev-folder.0.id, null) gke-prod = try(module.branch-gke-prod-folder.0.id, null) networking = try(module.branch-network-folder.id, null) @@ -226,6 +250,32 @@ locals { sa = module.branch-gke-prod-r-sa.0.email }) }, + !var.fast_features.gcve ? {} : { + "3-gcve-dev" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.branch-gcve-dev-gcs.0.name + name = "gcve-dev" + sa = module.branch-gcve-dev-sa.0.email + }) + "3-gcve-dev-r" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.branch-gcve-dev-gcs.0.name + name = "gcve-dev" + sa = module.branch-gcve-dev-r-sa.0.email + }) + "3-gcve-prod" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.branch-gcve-prod-gcs.0.name + name = "gcve-prod" + sa = module.branch-gcve-prod-sa.0.email + }) + "3-gcve-prod-r" = templatefile(local._tpl_providers, { + backend_extra = null + bucket = module.branch-gcve-prod-gcs.0.name + name = "gcve-prod" + sa = module.branch-gcve-prod-r-sa.0.email + }) + }, !var.fast_features.project_factory ? {} : { "3-project-factory-dev" = templatefile(local._tpl_providers, { backend_extra = null @@ -286,6 +336,10 @@ locals { data-platform-dev-r = try(module.branch-dp-dev-r-sa.0.email, null) data-platform-prod = try(module.branch-dp-prod-sa.0.email, null) data-platform-prod-r = try(module.branch-dp-prod-r-sa.0.email, null) + gcve-dev = try(module.branch-gcve-dev-sa.0.email, null) + gcve-dev-r = try(module.branch-gcve-dev-r-sa.0.email, null) + gcve-prod = try(module.branch-gcve-prod-sa.0.email, null) + gcve-prod-r = try(module.branch-gcve-prod-r-sa.0.email, null) gke-dev = try(module.branch-gke-dev-sa.0.email, null) gke-dev-r = try(module.branch-gke-dev-r-sa.0.email, null) gke-prod = try(module.branch-gke-prod-sa.0.email, null) @@ -364,6 +418,27 @@ output "dataplatform" { } } +output "gcve" { + # tfdoc:output:consumers 03-gke-multitenant + description = "Data for the GCVE stage." + value = ( + var.fast_features.gcve + ? { + "dev" = { + folder = module.branch-gcve-dev-folder.0.id + gcs_bucket = module.branch-gcve-dev-gcs.0.name + service_account = module.branch-gcve-dev-sa.0.email + } + "prod" = { + folder = module.branch-gcve-prod-folder.0.id + gcs_bucket = module.branch-gcve-prod-gcs.0.name + service_account = module.branch-gcve-prod-sa.0.email + } + } + : {} + ) +} + output "gke_multitenant" { # tfdoc:output:consumers 03-gke-multitenant description = "Data for the GKE multitenant stage." diff --git a/fast/stages/1-resman/variables.tf b/fast/stages/1-resman/variables.tf index 3987e81f..02ec155a 100644 --- a/fast/stages/1-resman/variables.tf +++ b/fast/stages/1-resman/variables.tf @@ -77,6 +77,18 @@ variable "cicd_repositories" { branch = optional(string) identity_provider = optional(string) })) + gcve_dev = optional(object({ + name = string + type = string + branch = optional(string) + identity_provider = optional(string) + })) + gcve_prod = optional(object({ + name = string + type = string + branch = optional(string) + identity_provider = optional(string) + })) networking = optional(object({ name = string type = string @@ -136,6 +148,7 @@ variable "custom_roles" { # tfdoc:variable:source 0-bootstrap description = "Custom roles defined at the org level, in key => id format." type = object({ + gcve_network_admin = string service_project_network_admin = string storage_viewer = string }) @@ -157,6 +170,7 @@ variable "fast_features" { type = object({ data_platform = optional(bool, false) gke = optional(bool, false) + gcve = optional(bool, false) project_factory = optional(bool, false) sandbox = optional(bool, false) teams = optional(bool, false) diff --git a/fast/stages/2-networking-a-peering/README.md b/fast/stages/2-networking-a-peering/README.md index 27272d0e..d8a686ca 100644 --- a/fast/stages/2-networking-a-peering/README.md +++ b/fast/stages/2-networking-a-peering/README.md @@ -389,21 +389,22 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS |---|---|:---:|:---:|:---:|:---:| | [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L116) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L126) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L142) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L126) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L136) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L152) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L72) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L82) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L89) | Email used for essential contacts, unset if null. | string | | null | | | [factories_config](variables.tf#L95) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L136) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [fast_features](variables.tf#L116) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | +| [outputs_location](variables.tf#L146) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | | [peering_configs](variables-peerings.tf#L19) | Peering configurations. | object({…}) | | {} | | -| [psa_ranges](variables.tf#L153) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | null | | -| [regions](variables.tf#L172) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L184) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L198) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [psa_ranges](variables.tf#L163) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | null | | +| [regions](variables.tf#L182) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L194) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L208) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-a-peering/net-dev.tf b/fast/stages/2-networking-a-peering/net-dev.tf index bce5883c..7f7e0e77 100644 --- a/fast/stages/2-networking-a-peering/net-dev.tf +++ b/fast/stages/2-networking-a-peering/net-dev.tf @@ -22,16 +22,23 @@ module "dev-spoke-project" { name = "dev-net-spoke-0" parent = var.folder_ids.networking-dev prefix = var.prefix - services = [ - "container.googleapis.com", - "compute.googleapis.com", - "dns.googleapis.com", - "iap.googleapis.com", - "networkmanagement.googleapis.com", - "servicenetworking.googleapis.com", - "stackdriver.googleapis.com", - "vpcaccess.googleapis.com" - ] + services = concat( + [ + "container.googleapis.com", + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkmanagement.googleapis.com", + "servicenetworking.googleapis.com", + "stackdriver.googleapis.com", + "vpcaccess.googleapis.com" + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-a-peering/net-prod.tf b/fast/stages/2-networking-a-peering/net-prod.tf index 66236c2b..dd4b532f 100644 --- a/fast/stages/2-networking-a-peering/net-prod.tf +++ b/fast/stages/2-networking-a-peering/net-prod.tf @@ -22,16 +22,23 @@ module "prod-spoke-project" { name = "prod-net-spoke-0" parent = var.folder_ids.networking-prod prefix = var.prefix - services = [ - "container.googleapis.com", - "compute.googleapis.com", - "dns.googleapis.com", - "iap.googleapis.com", - "networkmanagement.googleapis.com", - "servicenetworking.googleapis.com", - "stackdriver.googleapis.com", - "vpcaccess.googleapis.com" - ] + services = concat( + [ + "container.googleapis.com", + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkmanagement.googleapis.com", + "servicenetworking.googleapis.com", + "stackdriver.googleapis.com", + "vpcaccess.googleapis.com" + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-a-peering/variables.tf b/fast/stages/2-networking-a-peering/variables.tf index fc8255f3..46639d39 100644 --- a/fast/stages/2-networking-a-peering/variables.tf +++ b/fast/stages/2-networking-a-peering/variables.tf @@ -113,6 +113,16 @@ variable "factories_config" { } } +variable "fast_features" { + # tfdoc:variable:source 0-0-bootstrap + description = "Selective control for top-level FAST features." + type = object({ + gcve = optional(bool, false) + }) + default = {} + nullable = false +} + variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." diff --git a/fast/stages/2-networking-b-vpn/README.md b/fast/stages/2-networking-b-vpn/README.md index 708c9e94..6aa86f44 100644 --- a/fast/stages/2-networking-b-vpn/README.md +++ b/fast/stages/2-networking-b-vpn/README.md @@ -413,21 +413,22 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS |---|---|:---:|:---:|:---:|:---:| | [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L116) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L126) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L142) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L126) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L136) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L152) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L72) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L82) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L89) | Email used for essential contacts, unset if null. | string | | null | | | [factories_config](variables.tf#L95) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L136) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L153) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | null | | -| [regions](variables.tf#L172) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L184) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [fast_features](variables.tf#L116) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | +| [outputs_location](variables.tf#L146) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L163) | IP ranges used for Private Service Access (CloudSQL, etc.). | object({…}) | | null | | +| [regions](variables.tf#L182) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L194) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | | [vpn_configs](variables-vpn.tf#L17) | Hub to spokes VPN configurations. | object({…}) | | {} | | -| [vpn_onprem_primary_config](variables.tf#L198) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_primary_config](variables.tf#L208) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-b-vpn/net-dev.tf b/fast/stages/2-networking-b-vpn/net-dev.tf index bce5883c..7f7e0e77 100644 --- a/fast/stages/2-networking-b-vpn/net-dev.tf +++ b/fast/stages/2-networking-b-vpn/net-dev.tf @@ -22,16 +22,23 @@ module "dev-spoke-project" { name = "dev-net-spoke-0" parent = var.folder_ids.networking-dev prefix = var.prefix - services = [ - "container.googleapis.com", - "compute.googleapis.com", - "dns.googleapis.com", - "iap.googleapis.com", - "networkmanagement.googleapis.com", - "servicenetworking.googleapis.com", - "stackdriver.googleapis.com", - "vpcaccess.googleapis.com" - ] + services = concat( + [ + "container.googleapis.com", + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkmanagement.googleapis.com", + "servicenetworking.googleapis.com", + "stackdriver.googleapis.com", + "vpcaccess.googleapis.com" + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-b-vpn/net-prod.tf b/fast/stages/2-networking-b-vpn/net-prod.tf index 66236c2b..dd4b532f 100644 --- a/fast/stages/2-networking-b-vpn/net-prod.tf +++ b/fast/stages/2-networking-b-vpn/net-prod.tf @@ -22,16 +22,23 @@ module "prod-spoke-project" { name = "prod-net-spoke-0" parent = var.folder_ids.networking-prod prefix = var.prefix - services = [ - "container.googleapis.com", - "compute.googleapis.com", - "dns.googleapis.com", - "iap.googleapis.com", - "networkmanagement.googleapis.com", - "servicenetworking.googleapis.com", - "stackdriver.googleapis.com", - "vpcaccess.googleapis.com" - ] + services = concat( + [ + "container.googleapis.com", + "compute.googleapis.com", + "dns.googleapis.com", + "iap.googleapis.com", + "networkmanagement.googleapis.com", + "servicenetworking.googleapis.com", + "stackdriver.googleapis.com", + "vpcaccess.googleapis.com" + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-b-vpn/variables.tf b/fast/stages/2-networking-b-vpn/variables.tf index fc8255f3..46639d39 100644 --- a/fast/stages/2-networking-b-vpn/variables.tf +++ b/fast/stages/2-networking-b-vpn/variables.tf @@ -113,6 +113,16 @@ variable "factories_config" { } } +variable "fast_features" { + # tfdoc:variable:source 0-0-bootstrap + description = "Selective control for top-level FAST features." + type = object({ + gcve = optional(bool, false) + }) + default = {} + nullable = false +} + variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." diff --git a/fast/stages/2-networking-c-nva/README.md b/fast/stages/2-networking-c-nva/README.md index 31428b42..ab387b2a 100644 --- a/fast/stages/2-networking-c-nva/README.md +++ b/fast/stages/2-networking-c-nva/README.md @@ -458,23 +458,24 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS |---|---|:---:|:---:|:---:|:---:| | [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L116) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L149) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L165) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L126) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L159) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L175) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L72) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L82) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L89) | Email used for essential contacts, unset if null. | string | | null | | | [factories_config](variables.tf#L95) | Configuration for network resource factories. | object({…}) | | {…} | | -| [gcp_ranges](variables.tf#L126) | GCP address ranges in name => range format. | map(string) | | {…} | | -| [onprem_cidr](variables.tf#L141) | Onprem addresses in name => range format. | map(string) | | {…} | | -| [outputs_location](variables.tf#L159) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L176) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | -| [regions](variables.tf#L195) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L207) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L221) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | -| [vpn_onprem_secondary_config](variables.tf#L264) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | +| [fast_features](variables.tf#L116) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | +| [gcp_ranges](variables.tf#L136) | GCP address ranges in name => range format. | map(string) | | {…} | | +| [onprem_cidr](variables.tf#L151) | Onprem addresses in name => range format. | map(string) | | {…} | | +| [outputs_location](variables.tf#L169) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L186) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | +| [regions](variables.tf#L205) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L217) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L231) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L274) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-c-nva/net-dev.tf b/fast/stages/2-networking-c-nva/net-dev.tf index 739d012e..28b218b0 100644 --- a/fast/stages/2-networking-c-nva/net-dev.tf +++ b/fast/stages/2-networking-c-nva/net-dev.tf @@ -22,7 +22,7 @@ module "dev-spoke-project" { name = "dev-net-spoke-0" parent = var.folder_ids.networking-dev prefix = var.prefix - services = [ + services = concat([ "container.googleapis.com", "compute.googleapis.com", "dns.googleapis.com", @@ -31,7 +31,13 @@ module "dev-spoke-project" { "servicenetworking.googleapis.com", "stackdriver.googleapis.com", "vpcaccess.googleapis.com" - ] + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-c-nva/net-prod.tf b/fast/stages/2-networking-c-nva/net-prod.tf index fa9042e2..81af9967 100644 --- a/fast/stages/2-networking-c-nva/net-prod.tf +++ b/fast/stages/2-networking-c-nva/net-prod.tf @@ -22,7 +22,7 @@ module "prod-spoke-project" { name = "prod-net-spoke-0" parent = var.folder_ids.networking-prod prefix = var.prefix - services = [ + services = concat([ "container.googleapis.com", "compute.googleapis.com", "dns.googleapis.com", @@ -31,7 +31,13 @@ module "prod-spoke-project" { "servicenetworking.googleapis.com", "stackdriver.googleapis.com", "vpcaccess.googleapis.com" - ] + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-c-nva/variables.tf b/fast/stages/2-networking-c-nva/variables.tf index 2b4d8ac6..30654d6c 100644 --- a/fast/stages/2-networking-c-nva/variables.tf +++ b/fast/stages/2-networking-c-nva/variables.tf @@ -113,6 +113,16 @@ variable "factories_config" { } } +variable "fast_features" { + # tfdoc:variable:source 0-0-bootstrap + description = "Selective control for top-level FAST features." + type = object({ + gcve = optional(bool, false) + }) + default = {} + nullable = false +} + variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." diff --git a/fast/stages/2-networking-d-separate-envs/README.md b/fast/stages/2-networking-d-separate-envs/README.md index 8a8e838c..44c90731 100644 --- a/fast/stages/2-networking-d-separate-envs/README.md +++ b/fast/stages/2-networking-d-separate-envs/README.md @@ -332,21 +332,22 @@ Regions are defined via the `regions` variable which sets up a mapping between t |---|---|:---:|:---:|:---:|:---:| | [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L117) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L127) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L143) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L127) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L137) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L153) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L72) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L83) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L90) | Email used for essential contacts, unset if null. | string | | null | | | [factories_config](variables.tf#L96) | Configuration for network resource factories. | object({…}) | | {…} | | -| [outputs_location](variables.tf#L137) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L154) | IP ranges used for Private Service Access (e.g. CloudSQL). | object({…}) | | null | | -| [regions](variables.tf#L173) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L183) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_dev_primary_config](variables.tf#L197) | VPN gateway configuration for onprem interconnection from dev in the primary region. | object({…}) | | null | | -| [vpn_onprem_prod_primary_config](variables.tf#L240) | VPN gateway configuration for onprem interconnection from prod in the primary region. | object({…}) | | null | | +| [fast_features](variables.tf#L117) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | +| [outputs_location](variables.tf#L147) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L164) | IP ranges used for Private Service Access (e.g. CloudSQL). | object({…}) | | null | | +| [regions](variables.tf#L183) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L193) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_dev_primary_config](variables.tf#L207) | VPN gateway configuration for onprem interconnection from dev in the primary region. | object({…}) | | null | | +| [vpn_onprem_prod_primary_config](variables.tf#L250) | VPN gateway configuration for onprem interconnection from prod in the primary region. | object({…}) | | null | | ## Outputs diff --git a/fast/stages/2-networking-d-separate-envs/net-dev.tf b/fast/stages/2-networking-d-separate-envs/net-dev.tf index 24c9d4fa..5d880e08 100644 --- a/fast/stages/2-networking-d-separate-envs/net-dev.tf +++ b/fast/stages/2-networking-d-separate-envs/net-dev.tf @@ -22,7 +22,7 @@ module "dev-spoke-project" { name = "dev-net-spoke-0" parent = var.folder_ids.networking-dev prefix = var.prefix - services = [ + services = concat([ "container.googleapis.com", "compute.googleapis.com", "dns.googleapis.com", @@ -31,7 +31,13 @@ module "dev-spoke-project" { "servicenetworking.googleapis.com", "stackdriver.googleapis.com", "vpcaccess.googleapis.com" - ] + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true service_projects = [] diff --git a/fast/stages/2-networking-d-separate-envs/net-prod.tf b/fast/stages/2-networking-d-separate-envs/net-prod.tf index eea26bf1..af740c4f 100644 --- a/fast/stages/2-networking-d-separate-envs/net-prod.tf +++ b/fast/stages/2-networking-d-separate-envs/net-prod.tf @@ -22,7 +22,7 @@ module "prod-spoke-project" { name = "prod-net-spoke-0" parent = var.folder_ids.networking-prod prefix = var.prefix - services = [ + services = concat([ "container.googleapis.com", "compute.googleapis.com", "dns.googleapis.com", @@ -31,7 +31,13 @@ module "prod-spoke-project" { "servicenetworking.googleapis.com", "stackdriver.googleapis.com", "vpcaccess.googleapis.com" - ] + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true service_projects = [] diff --git a/fast/stages/2-networking-d-separate-envs/variables.tf b/fast/stages/2-networking-d-separate-envs/variables.tf index f1cb47e8..8beb8320 100644 --- a/fast/stages/2-networking-d-separate-envs/variables.tf +++ b/fast/stages/2-networking-d-separate-envs/variables.tf @@ -114,6 +114,16 @@ variable "factories_config" { } } +variable "fast_features" { + # tfdoc:variable:source 0-0-bootstrap + description = "Selective control for top-level FAST features." + type = object({ + gcve = optional(bool, false) + }) + default = {} + nullable = false +} + variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." diff --git a/fast/stages/2-networking-e-nva-bgp/README.md b/fast/stages/2-networking-e-nva-bgp/README.md index 1e81f9bf..3c9b2b98 100644 --- a/fast/stages/2-networking-e-nva-bgp/README.md +++ b/fast/stages/2-networking-e-nva-bgp/README.md @@ -484,25 +484,26 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS |---|---|:---:|:---:|:---:|:---:| | [automation](variables.tf#L42) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | | [billing_account](variables.tf#L50) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | -| [folder_ids](variables.tf#L116) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | -| [organization](variables.tf#L160) | Organization details. | object({…}) | ✓ | | 0-bootstrap | -| [prefix](variables.tf#L176) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L126) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [organization](variables.tf#L170) | Organization details. | object({…}) | ✓ | | 0-bootstrap | +| [prefix](variables.tf#L186) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | | [alert_config](variables.tf#L17) | Configuration for monitoring alerts. | object({…}) | | {…} | | | [custom_roles](variables.tf#L63) | Custom roles defined at the org level, in key => id format. | object({…}) | | null | 0-bootstrap | | [dns](variables.tf#L72) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L82) | Deploy Cloud NAT. | bool | | false | | | [essential_contacts](variables.tf#L89) | Email used for essential contacts, unset if null. | string | | null | | | [factories_config](variables.tf#L95) | Configuration for network resource factories. | object({…}) | | {…} | | -| [gcp_ranges](variables.tf#L126) | GCP address ranges in name => range format. | map(string) | | {…} | | -| [ncc_asn](variables.tf#L141) | The NCC Cloud Routers ASN configuration. | map(number) | | {…} | | -| [onprem_cidr](variables.tf#L152) | Onprem addresses in name => range format. | map(string) | | {…} | | -| [outputs_location](variables.tf#L170) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L187) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | -| [regions](variables.tf#L206) | Region definitions. | object({…}) | | {…} | | -| [service_accounts](variables.tf#L218) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L232) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | -| [vpn_onprem_secondary_config](variables.tf#L275) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | -| [zones](variables.tf#L318) | Zones in which NVAs are deployed. | list(string) | | ["b", "c"] | | +| [fast_features](variables.tf#L116) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | +| [gcp_ranges](variables.tf#L136) | GCP address ranges in name => range format. | map(string) | | {…} | | +| [ncc_asn](variables.tf#L151) | The NCC Cloud Routers ASN configuration. | map(number) | | {…} | | +| [onprem_cidr](variables.tf#L162) | Onprem addresses in name => range format. | map(string) | | {…} | | +| [outputs_location](variables.tf#L180) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L197) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | null | | +| [regions](variables.tf#L216) | Region definitions. | object({…}) | | {…} | | +| [service_accounts](variables.tf#L228) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | +| [vpn_onprem_primary_config](variables.tf#L242) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L285) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | +| [zones](variables.tf#L328) | Zones in which NVAs are deployed. | list(string) | | ["b", "c"] | | ## Outputs diff --git a/fast/stages/2-networking-e-nva-bgp/net-dev.tf b/fast/stages/2-networking-e-nva-bgp/net-dev.tf index 3b3c3efe..277ff8fc 100644 --- a/fast/stages/2-networking-e-nva-bgp/net-dev.tf +++ b/fast/stages/2-networking-e-nva-bgp/net-dev.tf @@ -22,7 +22,7 @@ module "dev-spoke-project" { name = "dev-net-spoke-0" parent = var.folder_ids.networking-dev prefix = var.prefix - services = [ + services = concat([ "compute.googleapis.com", "dns.googleapis.com", "iap.googleapis.com", @@ -30,7 +30,13 @@ module "dev-spoke-project" { "servicenetworking.googleapis.com", "stackdriver.googleapis.com", "vpcaccess.googleapis.com" - ] + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-e-nva-bgp/net-prod.tf b/fast/stages/2-networking-e-nva-bgp/net-prod.tf index 7e9d7e34..8ed524ba 100644 --- a/fast/stages/2-networking-e-nva-bgp/net-prod.tf +++ b/fast/stages/2-networking-e-nva-bgp/net-prod.tf @@ -22,7 +22,7 @@ module "prod-spoke-project" { name = "prod-net-spoke-0" parent = var.folder_ids.networking-prod prefix = var.prefix - services = [ + services = concat([ "compute.googleapis.com", "dns.googleapis.com", "iap.googleapis.com", @@ -30,7 +30,13 @@ module "prod-spoke-project" { "servicenetworking.googleapis.com", "stackdriver.googleapis.com", "vpcaccess.googleapis.com" - ] + ], + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] + ) + ) shared_vpc_host_config = { enabled = true } diff --git a/fast/stages/2-networking-e-nva-bgp/variables.tf b/fast/stages/2-networking-e-nva-bgp/variables.tf index f92eefcd..8fd60c8b 100644 --- a/fast/stages/2-networking-e-nva-bgp/variables.tf +++ b/fast/stages/2-networking-e-nva-bgp/variables.tf @@ -113,6 +113,16 @@ variable "factories_config" { } } +variable "fast_features" { + # tfdoc:variable:source 0-0-bootstrap + description = "Selective control for top-level FAST features." + type = object({ + gcve = optional(bool, false) + }) + default = {} + nullable = false +} + variable "folder_ids" { # tfdoc:variable:source 1-resman description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." diff --git a/fast/stages/3-gcve/README.md b/fast/stages/3-gcve/README.md new file mode 100644 index 00000000..258fd9b9 --- /dev/null +++ b/fast/stages/3-gcve/README.md @@ -0,0 +1,34 @@ +# Google Cloud VMware Engine Stage + +The GCVE stage builds on top of your foundations to create and set up projects and related resources, used for your Google Cloud VMware Engine (GCVE) private cloud environments. +It is organized in folders representing environments (e.g. `dev`, `prod`), each implemented by a stand-alone Terraform setup. + +This directory contains a [GCVE single region private cloud for the `prod` environment](./prod/) that can be used as-is or cloned with few changes to implement further environments. Refer to the example [`prod`/README.md](./prod/README.md) for configuration details. + +With this stage and the [GCVE blueprints](./../../../blueprints/gcve/), you can rapidly deploy production-ready GCVE environments. These environments are fully optimized to integrate seamlessly with your Fabric FAST network topology. Explore the deployment patterns below to find the perfect fit for your use case." + +## TOC + + +- [TOC](#toc) +- [Deployment Patterns](#deployment-patterns) + - [Single Region](#single-region) + - [Standalone VPC for a sigle region GCVE deployment](#standalone-vpc-for-a-sigle-region-gcve-deployment) + - [Separate VPC Environments for individual dedicated GCVE deployments](#separate-vpc-environments-for-individual-dedicated-gcve-deployments) + + +## Deployment Patterns +### Single Region +#### Standalone VPC for a sigle region GCVE deployment +

+ Standalone Shared VPC for a sigle region GCVE deployment +

+ +#### Separate VPC Environments for individual dedicated GCVE deployments +

+ Separate VPC Environments for individual dedicated GCVE deployments +

+ + + + \ No newline at end of file diff --git a/fast/stages/3-gcve/diagram0.png b/fast/stages/3-gcve/diagram0.png new file mode 100644 index 00000000..fa1ee52f Binary files /dev/null and b/fast/stages/3-gcve/diagram0.png differ diff --git a/fast/stages/3-gcve/diagram1.png b/fast/stages/3-gcve/diagram1.png new file mode 100644 index 00000000..fe33ac6f Binary files /dev/null and b/fast/stages/3-gcve/diagram1.png differ diff --git a/fast/stages/3-gcve/prod/README.md b/fast/stages/3-gcve/prod/README.md new file mode 100644 index 00000000..4088ba45 --- /dev/null +++ b/fast/stages/3-gcve/prod/README.md @@ -0,0 +1,134 @@ +# GCVE Private Clouds for Production Environment + +This stage provides the Terraform content for the creation and management of multiple GCVE private clouds which are connected to an existing network. The network infrastructure needs to be deployed before executing this stage by executing the respective Fabric FAST stage (`2-networking-*`). This stage can be replicated for complex GCVE designs requiring a separate management of the network infrastructure (VEN) and the access domain or for other constraints which make you decide to have indipendent GCVE environments (e.g dev/prod, region or team serparation). + +## Design overview and choices + +> A more comprehensive description of the GCVE architecture and approach can be found in the [GCVE Private Cloud Minimal blueprint](../../../../blueprints/gcve/pc-minimal/). The blueprint is wrapped and configured here to leverage the FAST flow. + +The GCVE stage creates a project and all the expected resources in a well-defined context, usually an ad-hoc folder managed by the resource management stage. Resources are organized by environment within this folder. + + +## How to run this stage + +This stage is meant to be executed after the FAST "foundational" stages: bootstrap, resource management, security and networking stages. + +It is also possible to run this stage in isolation. Refer to the *[Running in isolation](#running-in-isolation)* section below for details. + +Before running this stage, you need to make sure you have the correct credentials and permissions, and localize variables by assigning values that match your configuration. + +### Provider and Terraform variables + +As all other FAST stages, the [mechanism used to pass variable values and pre-built provider files from one stage to the next](../../0-bootstrap/README.md#output-files-and-cross-stage-variables) is also leveraged here. + +The commands to link or copy the provider and terraform variable files can be easily derived from the `stage-links.sh` script in the FAST root folder, passing it a single argument with the local output files folder (if configured) or the GCS output bucket in the automation project (derived from stage 0 outputs). The following examples demonstrate both cases, and the resulting commands that then need to be copy/pasted and run. + +```bash +../../../stage-links.sh ~/fast-config + +# copy and paste the following commands for '3-gcve' + +ln -s ~/fast-config/providers/3-gcve-dev-providers.tf ./ +ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/0-bootstrap.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/1-resman.auto.tfvars.json ./ +ln -s ~/fast-config/tfvars/2-networking.auto.tfvars.json ./ +``` + +```bash +../../../stage-links.sh gs://xxx-prod-iac-core-outputs-0 + +# copy and paste the following commands for '3-gcve' + +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/providers/3-gcve-dev-providers.tf ./ +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-globals.auto.tfvars.json ./ +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/0-bootstrap.auto.tfvars.json ./ +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/1-resman.auto.tfvars.json ./ +gcloud alpha storage cp gs://xxx-prod-iac-core-outputs-0/tfvars/2-networking.auto.tfvars.json ./ +``` + +### Impersonating the automation service account + +The preconfigured provider file uses impersonation to run with this stage's automation service account's credentials. The `gcp-devops` and `organization-admins` groups have the necessary IAM bindings in place to do that, so make sure the current user is a member of one of those groups. + +### Variable configuration + +Variables in this stage -- like most other FAST stages -- are broadly divided into three separate sets: + +- variables which refer to global values for the whole organization (org id, billing account id, prefix, etc.), which are pre-populated via the `0-globals.auto.tfvars.json` file linked or copied above +- variables which refer to resources managed by previous stage, which are prepopulated here via the `*.auto.tfvars.json` files linked or copied above +- and finally variables that optionally control this stage's behaviour and customizations, and can to be set in a custom `terraform.tfvars` file + +The full list can be found in the [Variables](#variables) table at the bottom of this document. + +### Running the stage + +Once provider and variable values are in place and the correct user is configured, the stage can be run: + +```bash +terraform init +terraform apply +``` + +### Running in isolation + +This stage can be run in isolation by providing the necessary variables, but it's really meant to be used as part of the FAST flow after the "foundational stages" ([`0-bootstrap`](../../0-bootstrap), [`1-resman`](../../1-resman), [`2-networking`](../../2-networking-b-vpn). + +When running in isolation, the following roles are needed on the principal used to apply Terraform: + +- on the organization or network folder level + - `roles/xpnAdmin` or a custom role which includes the following permissions + - `"compute.organizations.enableXpnResource"`, + - `"compute.organizations.disableXpnResource"`, + - `"compute.subnetworks.setIamPolicy"`, +- on each folder where projects are created + - `"roles/logging.admin"` + - `"roles/owner"` + - `"roles/resourcemanager.folderAdmin"` + - `"roles/resourcemanager.projectCreator"` +- on the host project for the Shared VPC + - `"roles/browser"` + - `"roles/compute.viewer"` +- on the organization or billing account + - `roles/billing.admin` + +The VPC host project, VPC and subnets should already exist. + + + +## Files + +| name | description | modules | resources | +|---|---|---|---| +| [main.tf](./main.tf) | GCVE private cloud for development environment. | pc-minimal | | +| [outputs.tf](./outputs.tf) | Output variables. | | google_storage_bucket_object · local_file | +| [variables.tf](./variables.tf) | Module variables. | | | + +## Variables + +| name | description | type | required | default | producer | +|---|---|:---:|:---:|:---:|:---:| +| [automation](variables.tf#L17) | Automation resources created by the bootstrap stage. | object({…}) | ✓ | | 0-bootstrap | +| [billing_account](variables.tf#L25) | Billing account id. If billing account is not part of the same org set `is_org_level` to false. | object({…}) | ✓ | | 0-bootstrap | +| [folder_ids](variables.tf#L38) | Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created. | object({…}) | ✓ | | 1-resman | +| [host_project_ids](variables.tf#L59) | Host project for the shared VPC. | object({…}) | ✓ | | 2-networking | +| [organization](variables.tf#L80) | Organization details. | object({…}) | ✓ | | 00-globals | +| [prefix](variables.tf#L96) | Prefix used for resources that need unique names. Use 9 characters or less. | string | ✓ | | 0-bootstrap | +| [private_cloud_configs](variables.tf#L102) | The VMware private cloud configurations. The key is the unique private cloud name suffix. | map(object({…})) | ✓ | | | +| [vpc_self_links](variables.tf#L131) | Self link for the shared VPC. | object({…}) | ✓ | | 2-networking | +| [groups_gcve](variables.tf#L46) | GCVE groups. | object({…}) | | {…} | | +| [iam](variables.tf#L67) | Project-level authoritative IAM bindings for users and service accounts in {ROLE => [MEMBERS]} format. | map(list(string)) | | {} | | +| [labels](variables.tf#L74) | Project-level labels. | map(string) | | {} | | +| [outputs_location](variables.tf#L90) | Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable. | string | | null | | +| [project_services](variables.tf#L124) | Additional project services to enable. | list(string) | | [] | | + +## Outputs + +| name | description | sensitive | consumers | +|---|---|:---:|---| +| [project_id](outputs.tf#L46) | GCVE project id. | | | +| [vmw_engine_network_config](outputs.tf#L51) | VMware engine network configuration. | | | +| [vmw_engine_network_peerings](outputs.tf#L56) | The peerings created towards the user VPC or other VMware engine networks. | | | +| [vmw_engine_private_clouds](outputs.tf#L61) | VMware engine private cloud resources. | | | +| [vmw_private_cloud_network](outputs.tf#L66) | VMware engine network. | | | + diff --git a/fast/stages/3-gcve/prod/main.tf b/fast/stages/3-gcve/prod/main.tf new file mode 100644 index 00000000..98531493 --- /dev/null +++ b/fast/stages/3-gcve/prod/main.tf @@ -0,0 +1,59 @@ +/** + * Copyright 2024 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. + */ + +# tfdoc:file:description GCVE private cloud for development environment. +locals { + groups_gcve = { + for k, v in var.groups_gcve : k => ( + can(regex("^[a-zA-Z]+:", v)) + ? v + : "group:${v}@${var.organization.domain}" + ) + } + peer_network = { + for k, v in var.vpc_self_links : k => ( + trimprefix(v, "https://www.googleapis.com/compute/v1/") + ) + } +} + +module "gcve-pc" { + source = "../../../../blueprints/gcve/pc-minimal" + billing_account_id = var.billing_account.id + folder_id = var.folder_ids.gcve-prod + project_id = "gcve-0" + groups = local.groups_gcve + iam = var.iam + labels = merge(var.labels, { environment = "prod" }) + prefix = "${var.prefix}-prod" + project_services = var.project_services + + network_peerings = { + prod-spoke-ven = { + peer_network = local.peer_network.prod-spoke-0 + peer_project_id = var.host_project_ids.prod-spoke-0 + configure_peer_network = true + custom_routes = { + export_to_peer = true + import_from_peer = true + export_to_ven = true + import_from_ven = true + } + } + } + + private_cloud_configs = var.private_cloud_configs +} diff --git a/fast/stages/3-gcve/prod/outputs.tf b/fast/stages/3-gcve/prod/outputs.tf new file mode 100644 index 00000000..5c97be02 --- /dev/null +++ b/fast/stages/3-gcve/prod/outputs.tf @@ -0,0 +1,70 @@ +# Copyright 2024 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 +# +# https://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. + +# tfdoc:file:description Output variables. + +locals { + tfvars = { + project_ids = { + gcve-dev = module.gcve-pc.project_id + } + vmw_engine_network_config = module.gcve-pc.vmw_engine_network_config + vmw_engine_network_peerings = module.gcve-pc.vmw_engine_network_peerings + vmw_engine_private_clouds = module.gcve-pc.vmw_engine_private_clouds + vmw_private_cloud_network = module.gcve-pc.vmw_private_cloud_network + } +} + +# generate tfvars file for subsequent stages + +resource "local_file" "tfvars" { + for_each = var.outputs_location == null ? {} : { 1 = 1 } + file_permission = "0644" + filename = "${pathexpand(var.outputs_location)}/tfvars/3-gcve-dev.auto.tfvars.json" + content = jsonencode(local.tfvars) +} + +resource "google_storage_bucket_object" "tfvars" { + bucket = var.automation.outputs_bucket + name = "tfvars/3-gcve-dev.auto.tfvars.json" + content = jsonencode(local.tfvars) +} + +# outputs + +output "project_id" { + description = "GCVE project id." + value = module.gcve-pc.project_id +} + +output "vmw_engine_network_config" { + description = "VMware engine network configuration." + value = module.gcve-pc.vmw_engine_network_config +} + +output "vmw_engine_network_peerings" { + description = "The peerings created towards the user VPC or other VMware engine networks." + value = module.gcve-pc.vmw_engine_network_peerings +} + +output "vmw_engine_private_clouds" { + description = "VMware engine private cloud resources." + value = module.gcve-pc.vmw_engine_private_clouds +} + +output "vmw_private_cloud_network" { + description = "VMware engine network." + value = module.gcve-pc.vmw_private_cloud_network +} + diff --git a/fast/stages/3-gcve/prod/variables.tf b/fast/stages/3-gcve/prod/variables.tf new file mode 100644 index 00000000..ee48c970 --- /dev/null +++ b/fast/stages/3-gcve/prod/variables.tf @@ -0,0 +1,139 @@ +/** + * Copyright 2024 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 "automation" { + # tfdoc:variable:source 0-bootstrap + description = "Automation resources created by the bootstrap stage." + type = object({ + outputs_bucket = string + }) +} + +variable "billing_account" { + # tfdoc:variable:source 0-bootstrap + description = "Billing account id. If billing account is not part of the same org set `is_org_level` to false." + type = object({ + id = string + is_org_level = optional(bool, true) + }) + validation { + condition = var.billing_account.is_org_level != null + error_message = "Invalid `null` value for `billing_account.is_org_level`." + } +} + +variable "folder_ids" { + # tfdoc:variable:source 1-resman + description = "Folders to be used for the networking resources in folders/nnnnnnnnnnn format. If null, folder will be created." + type = object({ + gcve-prod = string + }) +} + +variable "groups_gcve" { + description = "GCVE groups." + type = object({ + gcp-gcve-admins = string + gcp-gcve-viewers = string + }) + default = { + gcp-gcve-admins = "gcp-gcve-admins" + gcp-gcve-viewers = "gcp-gcve-viewers" + } + nullable = false +} + +variable "host_project_ids" { + # tfdoc:variable:source 2-networking + description = "Host project for the shared VPC." + type = object({ + prod-spoke-0 = string + }) +} + +variable "iam" { + description = "Project-level authoritative IAM bindings for users and service accounts in {ROLE => [MEMBERS]} format." + type = map(list(string)) + default = {} + nullable = false +} + +variable "labels" { + description = "Project-level labels." + type = map(string) + default = {} +} + +variable "organization" { + # tfdoc:variable:source 00-globals + description = "Organization details." + type = object({ + domain = string + id = number + customer_id = string + }) +} + +variable "outputs_location" { + description = "Path where providers, tfvars files, and lists for the following stages are written. Leave empty to disable." + type = string + default = null +} + +variable "prefix" { + # tfdoc:variable:source 0-bootstrap + description = "Prefix used for resources that need unique names. Use 9 characters or less." + type = string +} + +variable "private_cloud_configs" { + description = "The VMware private cloud configurations. The key is the unique private cloud name suffix." + type = map(object({ + cidr = string + zone = string + # The key is the unique additional cluster name suffix + additional_cluster_configs = optional(map(object({ + custom_core_count = optional(number) + node_count = optional(number, 3) + node_type_id = optional(string, "standard-72") + })), {}) + management_cluster_config = optional(object({ + custom_core_count = optional(number) + name = optional(string, "mgmt-cluster") + node_count = optional(number, 3) + node_type_id = optional(string, "standard-72") + }), {}) + description = optional(string, "Managed by Terraform.") + })) + nullable = false +} + +variable "project_services" { + description = "Additional project services to enable." + type = list(string) + default = [] + nullable = false +} + +variable "vpc_self_links" { + # tfdoc:variable:source 2-networking + description = "Self link for the shared VPC." + type = object({ + prod-spoke-0 = string + }) +} + + diff --git a/modules/gcve-private-cloud/README.md b/modules/gcve-private-cloud/README.md index 1c4da1e5..1cc8029c 100644 --- a/modules/gcve-private-cloud/README.md +++ b/modules/gcve-private-cloud/README.md @@ -160,4 +160,5 @@ module "gcve-pc" { | [vmw_engine_network_peerings](outputs.tf#L22) | The peerings created towards the user VPC or other VMware engine networks. | | | [vmw_engine_network_policies](outputs.tf#L27) | The network policies associated to the VMware engine network. | | | [vmw_engine_private_clouds](outputs.tf#L32) | VMware engine private cloud resources. | | +| [vmw_private_cloud_network](outputs.tf#L37) | VMware engine network. | | diff --git a/modules/gcve-private-cloud/main.tf b/modules/gcve-private-cloud/main.tf index 7585109c..3f2e1ad4 100644 --- a/modules/gcve-private-cloud/main.tf +++ b/modules/gcve-private-cloud/main.tf @@ -92,6 +92,8 @@ resource "google_vmwareengine_private_cloud" "vmw_engine_private_clouds" { name = "${var.prefix}-${each.key}" description = each.value.description + type = each.value.management_cluster_config.node_count == 1 ? "TIME_LIMITED" : "STANDARD" + network_config { management_cidr = each.value.cidr vmware_engine_network = local.vmw_network.id diff --git a/modules/gcve-private-cloud/outputs.tf b/modules/gcve-private-cloud/outputs.tf index 06b444c3..832efc8b 100644 --- a/modules/gcve-private-cloud/outputs.tf +++ b/modules/gcve-private-cloud/outputs.tf @@ -33,3 +33,8 @@ output "vmw_engine_private_clouds" { description = "VMware engine private cloud resources." value = google_vmwareengine_private_cloud.vmw_engine_private_clouds } + +output "vmw_private_cloud_network" { + description = "VMware engine network." + value = google_vmwareengine_network.private_cloud_network.0 +} diff --git a/tests/fast/stages/s0_bootstrap/checklist.yaml b/tests/fast/stages/s0_bootstrap/checklist.yaml index c6f64409..466efd2f 100644 --- a/tests/fast/stages/s0_bootstrap/checklist.yaml +++ b/tests/fast/stages/s0_bootstrap/checklist.yaml @@ -364,7 +364,7 @@ counts: google_logging_project_bucket_config: 3 google_org_policy_policy: 20 google_organization_iam_binding: 27 - google_organization_iam_custom_role: 6 + google_organization_iam_custom_role: 7 google_organization_iam_member: 35 google_project: 3 google_project_iam_binding: 19 @@ -381,4 +381,4 @@ counts: google_tags_tag_key: 1 google_tags_tag_value: 1 modules: 17 - resources: 192 + resources: 193 diff --git a/tests/fast/stages/s0_bootstrap/simple.yaml b/tests/fast/stages/s0_bootstrap/simple.yaml index 0d7174df..ab916dd5 100644 --- a/tests/fast/stages/s0_bootstrap/simple.yaml +++ b/tests/fast/stages/s0_bootstrap/simple.yaml @@ -43,7 +43,7 @@ counts: google_logging_project_bucket_config: 3 google_org_policy_policy: 20 google_organization_iam_binding: 27 - google_organization_iam_custom_role: 6 + google_organization_iam_custom_role: 7 google_organization_iam_member: 22 google_project: 3 google_project_iam_binding: 19 @@ -61,10 +61,11 @@ counts: google_tags_tag_value: 1 local_file: 7 modules: 16 - resources: 183 + resources: 184 outputs: custom_roles: + gcve_network_admin: organizations/123456789012/roles/gcveNetworkAdmin organization_admin_viewer: organizations/123456789012/roles/organizationAdminViewer organization_iam_admin: organizations/123456789012/roles/organizationIamAdmin service_project_network_admin: organizations/123456789012/roles/serviceProjectNetworkAdmin diff --git a/tests/fast/stages/s1_resman/checklist.tfvars b/tests/fast/stages/s1_resman/checklist.tfvars index 9d0a4a70..bf3dd68e 100644 --- a/tests/fast/stages/s1_resman/checklist.tfvars +++ b/tests/fast/stages/s1_resman/checklist.tfvars @@ -13,6 +13,7 @@ billing_account = { } custom_roles = { # organization_iam_admin = "organizations/123456789012/roles/organizationIamAdmin", + gcve_network_admin = "organizations/123456789012/roles/gcveNetworkAdmin" service_project_network_admin = "organizations/123456789012/roles/xpnServiceAdmin" storage_viewer = "organizations/123456789012/roles/storageViewer" } diff --git a/tests/fast/stages/s1_resman/checklist.yaml b/tests/fast/stages/s1_resman/checklist.yaml index 7deb24f4..a5f4d964 100644 --- a/tests/fast/stages/s1_resman/checklist.yaml +++ b/tests/fast/stages/s1_resman/checklist.yaml @@ -416,7 +416,7 @@ values: counts: google_folder: 57 - google_folder_iam_binding: 67 + google_folder_iam_binding: 69 google_organization_iam_member: 5 google_project_iam_member: 4 google_service_account: 4 @@ -427,6 +427,6 @@ counts: google_storage_bucket_object: 5 google_tags_tag_binding: 5 google_tags_tag_key: 3 - google_tags_tag_value: 9 + google_tags_tag_value: 10 modules: 64 - resources: 173 + resources: 176 diff --git a/tests/fast/stages/s1_resman/simple.tfvars b/tests/fast/stages/s1_resman/simple.tfvars index d8bce3ae..63decb4f 100644 --- a/tests/fast/stages/s1_resman/simple.tfvars +++ b/tests/fast/stages/s1_resman/simple.tfvars @@ -13,6 +13,7 @@ billing_account = { } custom_roles = { # organization_iam_admin = "organizations/123456789012/roles/organizationIamAdmin", + gcve_network_admin = "organizations/123456789012/roles/gcveNetworkAdmin" service_project_network_admin = "organizations/123456789012/roles/xpnServiceAdmin" storage_viewer = "organizations/123456789012/roles/storageViewer" } diff --git a/tests/fast/stages/s1_resman/simple.yaml b/tests/fast/stages/s1_resman/simple.yaml index c4d09672..b0fea5a7 100644 --- a/tests/fast/stages/s1_resman/simple.yaml +++ b/tests/fast/stages/s1_resman/simple.yaml @@ -14,7 +14,7 @@ counts: google_folder: 5 - google_folder_iam_binding: 19 + google_folder_iam_binding: 21 google_organization_iam_member: 5 google_project_iam_member: 4 google_service_account: 4 @@ -25,6 +25,6 @@ counts: google_storage_bucket_object: 5 google_tags_tag_binding: 5 google_tags_tag_key: 3 - google_tags_tag_value: 9 + google_tags_tag_value: 10 modules: 12 - resources: 73 + resources: 76 diff --git a/tests/fast/stages/s3_gcve_minimal/__init__.py b/tests/fast/stages/s3_gcve_minimal/__init__.py new file mode 100644 index 00000000..7ba50f93 --- /dev/null +++ b/tests/fast/stages/s3_gcve_minimal/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2023 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. diff --git a/tests/fast/stages/s3_gcve_minimal/simple.tfvars b/tests/fast/stages/s3_gcve_minimal/simple.tfvars new file mode 100644 index 00000000..b27af097 --- /dev/null +++ b/tests/fast/stages/s3_gcve_minimal/simple.tfvars @@ -0,0 +1,53 @@ +automation = { + federated_identity_pool = null + federated_identity_providers = null + project_id = "fast-prod-automation" + project_number = 123456 + outputs_bucket = "test" + service_accounts = { + resman-r = "em-dev-gcve-0r@fast2-prod-iac-core-0.iam.gserviceaccount.com" + } +} + +billing_account = { + id = "000000-111111-222222" +} + +folder_ids = { + gcve-prod = "folders/00000000000000" +} + +groups_gcve = { + gcp-gcve-admins = "gcp-gcve-admins", + gcp-gcve-viewers = "gcp-gcve-viewers" +} + +host_project_ids = { + prod-spoke-0 = "prod-spoke-0" +} + +organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" +} + +prefix = "fast3" + +private_cloud_configs = { + dev-pc = { + cidr = "172.26.16.0/22" + zone = "europe-west8-a" + management_cluster_config = { + name = "mgmt-cluster" + node_count = 1 + node_type_id = "standard-72" + } + } +} + +vpc_self_links = { + "prod-spoke-0" = "https://www.googleapis.com/compute/v1/projects/em-prod-net-spoke-0/global/networks/prod-spoke-0", +} + + diff --git a/tests/fast/stages/s3_gcve_minimal/simple.yaml b/tests/fast/stages/s3_gcve_minimal/simple.yaml new file mode 100644 index 00000000..eb10f3c1 --- /dev/null +++ b/tests/fast/stages/s3_gcve_minimal/simple.yaml @@ -0,0 +1,25 @@ +# Copyright 2024 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. + +counts: + google_project: 1 + google_project_iam_binding: 2 + google_project_service: 1 + google_storage_bucket_object: 1 + google_vmwareengine_network: 1 + google_vmwareengine_network_peering: 2 + google_vmwareengine_private_cloud: 1 + modules: 3 + resources: 9 + diff --git a/tests/fast/stages/s3_gcve_minimal/tftest.yaml b/tests/fast/stages/s3_gcve_minimal/tftest.yaml new file mode 100644 index 00000000..f1342a6f --- /dev/null +++ b/tests/fast/stages/s3_gcve_minimal/tftest.yaml @@ -0,0 +1,22 @@ +# Copyright 2024 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. + +module: fast/stages/3-gcve/prod + +tests: + simple: + tfvars: + - simple.tfvars + inventory: + - simple.yaml