FAST GCVE stage (#2191)

* GCVE stage prerequisites

* fix gcve prereq

* gcve stage first deploy

* Updated readme filex

* docs updated

* some fixes after testing

* updated readme files

* elia fix

* gcve net admin custom role

* gcve net admin custom role

* elia fix

* ven peering deploy

* elia fix

* added blueprint and stage tests

* Edits to Readme files

* typo in outputs

* clean-up

* gcve stage tests fix

* readme fix

* fix sorting

* fix copyrights and readme file

* fix test

* fix copyright

* fixed gcve feature flag cond.

* removed validation

* fixed typo

* fixed typo

* fixed gcve tests

* fixed typo

* fixed typo

* fixed sorting

* fixed sorting

---------

Co-authored-by: Konrad Schieban <kschieban@google.com>
This commit is contained in:
Elia
2024-04-03 17:25:12 +02:00
committed by GitHub
parent fc23c9c387
commit b80132a618
58 changed files with 1779 additions and 164 deletions

View File

@@ -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.
<p align="center">
<img src="diagram.png" alt="GCVE single region private cloud">
</p>
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
```
<!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC -->
## Files
| name | description | modules | resources |
|---|---|---|---|
| [gcve-pc.tf](./gcve-pc.tf) | GCVE private cloud. | <code>gcve-private-cloud</code> | <code>google_vmwareengine_network_peering</code> |
| [main.tf](./main.tf) | Project. | <code>project</code> | |
| [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. | <code>string</code> | ✓ | |
| [folder_id](variables.tf#L22) | Folder used for the GCVE project in folders/nnnnnnnnnnn format. | <code>string</code> | ✓ | |
| [groups](variables.tf#L27) | GCVE groups. | <code title="object&#40;&#123;&#10; gcp-gcve-admins &#61; string&#10; gcp-gcve-viewers &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ | |
| [prefix](variables.tf#L81) | Prefix used for resource names. | <code>string</code> | ✓ | |
| [private_cloud_configs](variables.tf#L90) | The VMware private cloud configurations. The key is the unique private cloud name suffix. | <code title="map&#40;object&#40;&#123;&#10; cidr &#61; string&#10; zone &#61; string&#10; additional_cluster_configs &#61; optional&#40;map&#40;object&#40;&#123;&#10; custom_core_count &#61; optional&#40;number&#41;&#10; node_count &#61; optional&#40;number, 3&#41;&#10; node_type_id &#61; optional&#40;string, &#34;standard-72&#34;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; management_cluster_config &#61; optional&#40;object&#40;&#123;&#10; custom_core_count &#61; optional&#40;number&#41;&#10; name &#61; optional&#40;string, &#34;mgmt-cluster&#34;&#41;&#10; node_count &#61; optional&#40;number, 3&#41;&#10; node_type_id &#61; optional&#40;string, &#34;standard-72&#34;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; description &#61; optional&#40;string, &#34;Managed by Terraform.&#34;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | ✓ | |
| [project_id](variables.tf#L112) | ID of the project that will contain the GCVE private cloud. | <code>string</code> | ✓ | |
| [iam](variables.tf#L36) | Project-level authoritative IAM bindings for users and service accounts in {ROLE => [MEMBERS]} format. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [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. | <code>map&#40;list&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [labels](variables.tf#L50) | Project-level labels. | <code>map&#40;string&#41;</code> | | <code>&#123;&#125;</code> |
| [network_peerings](variables.tf#L56) | The network peerings between users' VPCs and the VMware Engine networks. The key is the peering name suffix. | <code title="map&#40;object&#40;&#123;&#10; peer_network &#61; string&#10; configure_peer_network &#61; optional&#40;bool, false&#41;&#10; custom_routes &#61; optional&#40;object&#40;&#123;&#10; export_to_peer &#61; optional&#40;bool, false&#41;&#10; import_from_peer &#61; optional&#40;bool, false&#41;&#10; export_to_ven &#61; optional&#40;bool, false&#41;&#10; import_from_ven &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; custom_routes_with_public_ip &#61; optional&#40;object&#40;&#123;&#10; export_to_peer &#61; optional&#40;bool, false&#41;&#10; import_from_peer &#61; optional&#40;bool, false&#41;&#10; export_to_ven &#61; optional&#40;bool, false&#41;&#10; import_from_ven &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; description &#61; optional&#40;string, &#34;Managed by Terraform.&#34;&#41;&#10; peer_project_id &#61; optional&#40;string&#41;&#10; peer_to_vmware_engine_network &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [project_services](variables.tf#L117) | Additional project services to enable. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
<!-- END TFDOC -->

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View File

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

View File

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

View File

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

View File

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