From 72da12e377a46f8a5f01ee84d88f44906c8237ce Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 12 Sep 2022 09:56:25 +0200 Subject: [PATCH 01/16] Rename gke-serverless to gke and add test for fast gke stage --- blueprints/{gke-serverless => gke}/README.md | 0 .../multitenant-fleet/README.md | 0 .../multitenant-fleet/diagram.png | Bin .../multitenant-fleet/gke-clusters.tf | 0 .../multitenant-fleet/gke-hub.tf | 0 .../multitenant-fleet/gke-nodepools.tf | 0 .../multitenant-fleet/main.tf | 0 .../multitenant-fleet/outputs.tf | 0 .../multitenant-fleet/variables.tf | 0 fast/stages/03-gke-multitenant/dev/main.tf | 2 +- tests/fast/conftest.py | 24 +++++-- .../__init__.py | 0 .../s03_gke_multitenant/fixture/main.tf | 65 ++++++++++++++++++ .../stages/s03_gke_multitenant/test_plan.py | 20 ++++++ 14 files changed, 103 insertions(+), 8 deletions(-) rename blueprints/{gke-serverless => gke}/README.md (100%) rename blueprints/{gke-serverless => gke}/multitenant-fleet/README.md (100%) rename blueprints/{gke-serverless => gke}/multitenant-fleet/diagram.png (100%) rename blueprints/{gke-serverless => gke}/multitenant-fleet/gke-clusters.tf (100%) rename blueprints/{gke-serverless => gke}/multitenant-fleet/gke-hub.tf (100%) rename blueprints/{gke-serverless => gke}/multitenant-fleet/gke-nodepools.tf (100%) rename blueprints/{gke-serverless => gke}/multitenant-fleet/main.tf (100%) rename blueprints/{gke-serverless => gke}/multitenant-fleet/outputs.tf (100%) rename blueprints/{gke-serverless => gke}/multitenant-fleet/variables.tf (100%) rename tests/fast/stages/{s03_data_platform/fixture => s03_gke_multitenant}/__init__.py (100%) create mode 100644 tests/fast/stages/s03_gke_multitenant/fixture/main.tf create mode 100644 tests/fast/stages/s03_gke_multitenant/test_plan.py diff --git a/blueprints/gke-serverless/README.md b/blueprints/gke/README.md similarity index 100% rename from blueprints/gke-serverless/README.md rename to blueprints/gke/README.md diff --git a/blueprints/gke-serverless/multitenant-fleet/README.md b/blueprints/gke/multitenant-fleet/README.md similarity index 100% rename from blueprints/gke-serverless/multitenant-fleet/README.md rename to blueprints/gke/multitenant-fleet/README.md diff --git a/blueprints/gke-serverless/multitenant-fleet/diagram.png b/blueprints/gke/multitenant-fleet/diagram.png similarity index 100% rename from blueprints/gke-serverless/multitenant-fleet/diagram.png rename to blueprints/gke/multitenant-fleet/diagram.png diff --git a/blueprints/gke-serverless/multitenant-fleet/gke-clusters.tf b/blueprints/gke/multitenant-fleet/gke-clusters.tf similarity index 100% rename from blueprints/gke-serverless/multitenant-fleet/gke-clusters.tf rename to blueprints/gke/multitenant-fleet/gke-clusters.tf diff --git a/blueprints/gke-serverless/multitenant-fleet/gke-hub.tf b/blueprints/gke/multitenant-fleet/gke-hub.tf similarity index 100% rename from blueprints/gke-serverless/multitenant-fleet/gke-hub.tf rename to blueprints/gke/multitenant-fleet/gke-hub.tf diff --git a/blueprints/gke-serverless/multitenant-fleet/gke-nodepools.tf b/blueprints/gke/multitenant-fleet/gke-nodepools.tf similarity index 100% rename from blueprints/gke-serverless/multitenant-fleet/gke-nodepools.tf rename to blueprints/gke/multitenant-fleet/gke-nodepools.tf diff --git a/blueprints/gke-serverless/multitenant-fleet/main.tf b/blueprints/gke/multitenant-fleet/main.tf similarity index 100% rename from blueprints/gke-serverless/multitenant-fleet/main.tf rename to blueprints/gke/multitenant-fleet/main.tf diff --git a/blueprints/gke-serverless/multitenant-fleet/outputs.tf b/blueprints/gke/multitenant-fleet/outputs.tf similarity index 100% rename from blueprints/gke-serverless/multitenant-fleet/outputs.tf rename to blueprints/gke/multitenant-fleet/outputs.tf diff --git a/blueprints/gke-serverless/multitenant-fleet/variables.tf b/blueprints/gke/multitenant-fleet/variables.tf similarity index 100% rename from blueprints/gke-serverless/multitenant-fleet/variables.tf rename to blueprints/gke/multitenant-fleet/variables.tf diff --git a/fast/stages/03-gke-multitenant/dev/main.tf b/fast/stages/03-gke-multitenant/dev/main.tf index a6ed3d47..db4fe092 100644 --- a/fast/stages/03-gke-multitenant/dev/main.tf +++ b/fast/stages/03-gke-multitenant/dev/main.tf @@ -17,7 +17,7 @@ # tfdoc:file:description GKE multitenant for development environment. module "gke-multitenant" { - source = "../../../../blueprints/gke-serverless/multitenant-fleet" + source = "../../../../blueprints/gke/multitenant-fleet" billing_account_id = var.billing_account.id folder_id = var.folder_ids.gke-dev project_id = "gke-clusters-0" diff --git a/tests/fast/conftest.py b/tests/fast/conftest.py index d96af5dc..976637eb 100644 --- a/tests/fast/conftest.py +++ b/tests/fast/conftest.py @@ -11,7 +11,6 @@ # 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. - "Shared fixtures" import inspect @@ -21,28 +20,39 @@ import types import pytest import tftest - BASEDIR = os.path.dirname(os.path.dirname(__file__)) @pytest.fixture(scope='session') def fast_e2e_plan_runner(_plan_runner): "Plan runner for end-to-end root module, returns modules and resources." + def run_plan(fixture_path=None, targets=None, refresh=True, include_bare_resources=False, compute_sums=True, **tf_vars): "Runs Terraform plan on a root module using defaults, returns data." plan = _plan_runner(fixture_path, targets=targets, refresh=refresh, **tf_vars) root_module = plan.root_module['child_modules'][0] - modules = { - m['address'].removeprefix(root_module['address'])[1:]: m['resources'] - for m in root_module['child_modules'] - } + + # Count all modules and resources up to 2 levels deep. We include + # the second level to account for wrapper modules used by stages 3 + modules = {} + for m in root_module['child_modules']: + key = m['address'].removeprefix(root_module['address'])[1:] + modules[key] = m.get('resources', []) + if m.get('child_modules'): + for m2 in m['child_modules']: + key2 = m2['address'].removeprefix(root_module['address'])[1:] + modules[key2] = m2.get('resources', []) + resources = [r for m in modules.values() for r in m] if include_bare_resources: bare_resources = root_module['resources'] resources.extend(bare_resources) if compute_sums: - return len(modules), len(resources), {k: len(v) for k, v in modules.items()} + return len(modules), len(resources), { + k: len(v) for k, v in modules.items() + } return modules, resources + return run_plan diff --git a/tests/fast/stages/s03_data_platform/fixture/__init__.py b/tests/fast/stages/s03_gke_multitenant/__init__.py similarity index 100% rename from tests/fast/stages/s03_data_platform/fixture/__init__.py rename to tests/fast/stages/s03_gke_multitenant/__init__.py diff --git a/tests/fast/stages/s03_gke_multitenant/fixture/main.tf b/tests/fast/stages/s03_gke_multitenant/fixture/main.tf new file mode 100644 index 00000000..2ee6e4f3 --- /dev/null +++ b/tests/fast/stages/s03_gke_multitenant/fixture/main.tf @@ -0,0 +1,65 @@ +/** + * 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: Data platform stage test + +module "stage" { + source = "../../../../../fast/stages/03-gke-multitenant/dev/" + automation = { + outputs_bucket = "test" + } + billing_account = { + id = "012345-67890A-BCDEF0", + organization_id = 123456 + } + clusters = { + mycluster = { + cluster_autoscaling = null + description = "My cluster" + dns_domain = null + location = "europe-west1" + labels = {} + net = { + master_range = "172.17.16.0/28" + pods = "pods" + services = "services" + subnet = "projects/my-host-project-id/regions/europe-west1/subnetworks/mycluster-subnet" + } + overrides = null + } + } + nodepools = { + mycluster = { + mynodepool = { + initial_node_count = 1 + node_count = 1 + node_type = "n2-standard-4" + overrides = null + spot = false + } + } + } + folder_ids = { + gke-dev = "folders/12345678" + } + host_project_ids = { + dev-spoke-0 = "fast-dev-net-spoke-0" + } + prefix = "fast" + vpc_self_links = { + dev-spoke-0 = "projects/fast-dev-net-spoke-0/global/networks/dev-spoke-0" + } +} diff --git a/tests/fast/stages/s03_gke_multitenant/test_plan.py b/tests/fast/stages/s03_gke_multitenant/test_plan.py new file mode 100644 index 00000000..6189f62e --- /dev/null +++ b/tests/fast/stages/s03_gke_multitenant/test_plan.py @@ -0,0 +1,20 @@ +# 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. + + +def test_counts(fast_e2e_plan_runner): + "Test stage." + num_modules, num_resources, _ = fast_e2e_plan_runner() + # TODO: to re-enable per-module resource count check print _, then test + assert num_modules > 0 and num_resources > 0 From 61c45501ab156f68ae52ed9ea6eb683bd9c63bfa Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 12 Sep 2022 10:00:38 +0200 Subject: [PATCH 02/16] Link shared vpc gke blueprint in gke folder --- blueprints/gke/shared-vpc-gke | 1 + 1 file changed, 1 insertion(+) create mode 120000 blueprints/gke/shared-vpc-gke diff --git a/blueprints/gke/shared-vpc-gke b/blueprints/gke/shared-vpc-gke new file mode 120000 index 00000000..e7fc85e6 --- /dev/null +++ b/blueprints/gke/shared-vpc-gke @@ -0,0 +1 @@ +../networking/shared-vpc-gke \ No newline at end of file From faf6c48a8032ecac4a2d235a6ab4dd8e31b2df1e Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 12 Sep 2022 10:03:27 +0200 Subject: [PATCH 03/16] Add gke description --- blueprints/gke/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/blueprints/gke/README.md b/blueprints/gke/README.md index 0077f7bd..12ac62b7 100644 --- a/blueprints/gke/README.md +++ b/blueprints/gke/README.md @@ -10,3 +10,10 @@ They are meant to be used as minimal but complete starting points to create actu This [blueprint](./multitenant-fleet/) allows simple centralized management of similar sets of GKE clusters and their nodepools in a single project, and optional fleet management via GKE Hub templated configurations.
+ +### Shared VPC with GKE and per-subnet support + + This [blueprint](../networking/shared-vpc-gke/) shows how to configure a Shared VPC, including the specific IAM configurations needed for GKE, and to give different level of access to the VPC subnets to different identities. + +It is meant to be used as a starting point for most Shared VPC configurations, and to be integrated to the above blueprints where Shared VPC is needed in more complex network topologies. +
From 014c2c7d103ab85fba65f16d47ca309a4d1c8e22 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 12 Sep 2022 10:09:56 +0200 Subject: [PATCH 04/16] Add readme to serverless blueprints folder --- blueprints/gke/README.md | 4 ++-- blueprints/serverless/README.md | 12 ++++++++++++ blueprints/serverless/api-gateway/README.md | 2 +- .../api-gateway/{architecture.png => diagram.png} | Bin 4 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 blueprints/serverless/README.md rename blueprints/serverless/api-gateway/{architecture.png => diagram.png} (100%) diff --git a/blueprints/gke/README.md b/blueprints/gke/README.md index 12ac62b7..307c9e23 100644 --- a/blueprints/gke/README.md +++ b/blueprints/gke/README.md @@ -1,6 +1,6 @@ -# GKE and Serverless blueprints +# GKE blueprints -The blueprints in this folder show implement **end-to-end scenarios** for GKE or Serveless topologies that show how to automate common configurations or leverage specific products. +The blueprints in this folder show implement **end-to-end scenarios** for GKE topologies that show how to automate common configurations or leverage specific products. They are meant to be used as minimal but complete starting points to create actual infrastructure, and as playgrounds to experiment with Google Cloud features. diff --git a/blueprints/serverless/README.md b/blueprints/serverless/README.md new file mode 100644 index 00000000..44f39f0f --- /dev/null +++ b/blueprints/serverless/README.md @@ -0,0 +1,12 @@ +# Serverless blueprints + +The blueprints in this folder show implement **end-to-end scenarios** for Serveless topologies that show how to automate common configurations or leverage specific products. + +They are meant to be used as minimal but complete starting points to create actual infrastructure, and as playgrounds to experiment with Google Cloud features. + +## Blueprints + +### Multi-region deployments for API Gateway + + This [blueprint](./api-gateway/) shows how to configure a load balancer to enable multi-region deployments for API Gateway. For more details on how this set up work have a look at the article [here](https://cloud.google.com/api-gateway/docs/multi-region-deployment) +
diff --git a/blueprints/serverless/api-gateway/README.md b/blueprints/serverless/api-gateway/README.md index fa350efb..9e17b11e 100644 --- a/blueprints/serverless/api-gateway/README.md +++ b/blueprints/serverless/api-gateway/README.md @@ -4,7 +4,7 @@ This tutorial shows you how to configure an HTTP(S) load balancer to enable mult The diagram below depicts the architecture that this blueprint sets up. -![Architecture](architecture.png) +![Architecture diagram](diagram.png) # Running the blueprint diff --git a/blueprints/serverless/api-gateway/architecture.png b/blueprints/serverless/api-gateway/diagram.png similarity index 100% rename from blueprints/serverless/api-gateway/architecture.png rename to blueprints/serverless/api-gateway/diagram.png From 20d5ebac40aabab98145fe0014cd3e1e8f1244bb Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 12 Sep 2022 10:11:35 +0200 Subject: [PATCH 05/16] Remove foundations blueprints in favor of FAST --- blueprints/README.md | 1 - blueprints/foundations/README.md | 48 ----- .../foundations/business-units/README.md | 59 ------ .../business-units/backend.tf.sample | 22 --- .../foundations/business-units/diagram.png | Bin 64603 -> 0 bytes blueprints/foundations/business-units/main.tf | 175 ------------------ .../foundations/business-units/outputs.tf | 75 -------- .../business-units/terraform.tfvars.sample | 19 -- .../foundations/business-units/variables.tf | 91 --------- .../foundations/business-units/versions.tf | 29 --- blueprints/foundations/environments/README.md | 65 ------- .../environments/backend.tf.sample | 23 --- .../foundations/environments/diagram.png | Bin 57325 -> 0 bytes blueprints/foundations/environments/locals.tf | 50 ----- blueprints/foundations/environments/main.tf | 168 ----------------- .../foundations/environments/outputs.tf | 61 ------ .../foundations/environments/variables.tf | 122 ------------ .../foundations/environments/versions.tf | 29 --- 18 files changed, 1037 deletions(-) delete mode 100644 blueprints/foundations/README.md delete mode 100644 blueprints/foundations/business-units/README.md delete mode 100644 blueprints/foundations/business-units/backend.tf.sample delete mode 100644 blueprints/foundations/business-units/diagram.png delete mode 100644 blueprints/foundations/business-units/main.tf delete mode 100644 blueprints/foundations/business-units/outputs.tf delete mode 100644 blueprints/foundations/business-units/terraform.tfvars.sample delete mode 100644 blueprints/foundations/business-units/variables.tf delete mode 100644 blueprints/foundations/business-units/versions.tf delete mode 100644 blueprints/foundations/environments/README.md delete mode 100644 blueprints/foundations/environments/backend.tf.sample delete mode 100644 blueprints/foundations/environments/diagram.png delete mode 100644 blueprints/foundations/environments/locals.tf delete mode 100644 blueprints/foundations/environments/main.tf delete mode 100644 blueprints/foundations/environments/outputs.tf delete mode 100644 blueprints/foundations/environments/variables.tf delete mode 100644 blueprints/foundations/environments/versions.tf diff --git a/blueprints/README.md b/blueprints/README.md index 5c2705c7..d75ef693 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -7,7 +7,6 @@ Currently available blueprints: - **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](./cloud-operations/asset-inventory-feed-remediation), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Granular Cloud DNS IAM for Shared VPC](./cloud-operations/dns-shared-vpc), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Packer image builder](./cloud-operations/packer-image-builder), [On-prem SA key management](./cloud-operations/onprem-sa-key-management), [TCP healthcheck for unmanaged GCE instances](./cloud-operations/unmanaged-instances-healthcheck), [HTTP Load Balancer with Cloud Armor](./cloud-operations/glb_and_armor) - **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/gcs-to-bq-with-least-privileges/), [Cloud Storage to Bigquery with Cloud Dataflow with least privileges](./data-solutions/gcs-to-bq-with-least-privileges/), [Data Platform Foundations](./data-solutions/data-platform-foundations/), [SQL Server AlwaysOn availability groups blueprint](./data-solutions/sqlserver-alwayson), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion/) - **factories** - [The why and the how of resource factories](./factories/README.md) -- **foundations** - [single level hierarchy](./foundations/environments/) (environments), [multiple level hierarchy](./foundations/business-units/) (business units + environments) - **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop), [PSC for on-premises Cloud Function invocation](./networking/private-cloud-function-from-onprem/), [decentralized firewall](./networking/decentralized-firewall) - **third party solutions** - [OpenShift cluster on Shared VPC](./third-party-solutions/openshift) diff --git a/blueprints/foundations/README.md b/blueprints/foundations/README.md deleted file mode 100644 index b3164578..00000000 --- a/blueprints/foundations/README.md +++ /dev/null @@ -1,48 +0,0 @@ -# Cloud foundation blueprints - -The blueprints in this folder deal with cloud foundations: the set of resources used to **create the organizational hierarchy** (folders and specific IAM roles), **implement top-level initial best practices** (audit log exports, policies) and **bootstrap infrastructure automation** (GCS buckets, service accounts and IAM roles). - -The blueprints are derived from actual production use cases, and are meant to be used as-is, or extended to create more complex hierarchies. The guiding principles they implement are: - -- divide the hierarchy in separate partitions along environment/organization boundaries, to enforce separation of duties and decouple organization admin permissions from the day-to-day running of infrastructure -- keep top-level Terraform code minimal and encapsulate complexity in modules, to ensure readability and allow using code as high level documentation - -## Blueprints - -### Environment Hierarchy - - This [blueprint](./environments/) implements a simple one-level organizational layout, which is commonly used to bootstrap small infrastructures, or in situations where lower level folders are managed with separate, more granular Terraform setups. - -One authoritative service account, one bucket and one folder are created for each environment, together with top-level shared resources. This blueprint's simplicity makes it a good starting point to understand and prototype foundational design. - -
- -### Business Unit / Environment Hierarchy - - This [blueprint](./business-units/) implements a two-level organizational layout, with a first level usually mapped to business units, and a second level implementing identical environments (prod, test, etc.) under each first-level folder. - -This approach maps well to medium sized infrastructures, and can be used as a starting point for more complex scenarios. Separate Terraform stages are then usually implemented for each business unit, implementing fine-grained project and service account creation for individual application teams. -
- -## Operational considerations - -These blueprints are always used manually, as they require very high-level permissions and are updated infrequently. - -The IAM roles needed are: - -- Project Creator, Folder Administrator, Logging Administrator on the root node (org or folder) -- Billing Account Administrator on the billing account or org -- Organization Administrator if Shared VPC roles have to be granted to the automation service accounts created for each scope - -State is local on the first run, then it should be moved to the GCS bucket created by the blueprints for this specific purpose: - -```bash -# first apply -terraform apply -# create backend file -cp backend.tf.sample backend.tf -# edit backend.tf and use bootstrap_tf_gcs_bucket output for GCS bucket name -vi backend.tf -# once done, move local state to GCS bucket -terraform init -``` diff --git a/blueprints/foundations/business-units/README.md b/blueprints/foundations/business-units/README.md deleted file mode 100644 index f75d44f9..00000000 --- a/blueprints/foundations/business-units/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# Business-units based organizational sample - -This sample creates an organizational layout with two folder levels, where the first level is usually mapped to one business unit or team (infra, data, analytics) and the second level represents environments (prod, test). It also sets up all prerequisites for automation (GCS state buckets, service accounts, etc.), and the correct roles on those to enforce separation of duties at the environment level. - -This layout is well suited for medium-sized infrastructures managed by different sets of teams, and in cases where the core infrastructure is managed centrally, as the top-level automation service accounts for each environment allow cross-team management of the base resources (projects, IAM, etc.). - -![High-level diagram](diagram.png "High-level diagram") - -Refer to the [section-level README](../README.md) for general considerations about this type of samples, and usage instructions. - -## Managed resources and services - -This sample creates several distinct groups of resources: - -- one top-level folder per business unit/team -- one top-level folder for shared services -- one second-level folder for each environment in all the business unit top-level folders -- one project in the shared folder to hold Terraform-related resources -- one project in the shared folder to set up and host centralized audit log exports -- one project in the shared folder to hold services used across environments like GCS, GCR, KMS, Cloud Build, etc. - -The number of resources in this sample is kept to a minimum so as to make it generally applicable, more resources can be easily added by leveraging other [modules from our bundle](../../../modules/), or from other sources like the [CFT suite](https://github.com/terraform-google-modules). - -## Shared services - -This sample uses a top-level folder to encapsulate projects that host resources that are not specific to a single environment. If no shared services are needed,the Terraform and audit modules can be easily attached to the root node, and the shared services folder and project removed from `main.tf`. - - -## Variables - -| name | description | type | required | default | -|---|---|:---:|:---:|:---:| -| [billing_account_id](variables.tf#L27) | Billing account id used as default for new projects. | string | ✓ | | -| [organization_id](variables.tf#L69) | Organization id in organizations/nnnnnnn format. | string | ✓ | | -| [prefix](variables.tf#L74) | Prefix used for resources that need unique names. | string | ✓ | | -| [root_node](variables.tf#L88) | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| [audit_filter](variables.tf#L17) | Audit log filter used for the log sink. | string | | | -| [environments](variables.tf#L32) | Environment short names. | map(string) | | {…} | -| [gcs_defaults](variables.tf#L42) | Defaults use for the state GCS buckets. | map(string) | | {…} | -| [iam_audit_viewers](variables.tf#L51) | Audit project viewers, in IAM format. | list(string) | | [] | -| [iam_shared_owners](variables.tf#L57) | Shared services project owners, in IAM format. | list(string) | | [] | -| [iam_terraform_owners](variables.tf#L63) | Terraform project owners, in IAM format. | list(string) | | [] | -| [project_services](variables.tf#L79) | Service APIs enabled by default in new projects. | list(string) | | […] | - -## Outputs - -| name | description | sensitive | -|---|---|:---:| -| [audit_logs_project](outputs.tf#L17) | Project that holds the audit logs export resources. | | -| [bootstrap_tf_gcs_bucket](outputs.tf#L22) | GCS bucket used for the bootstrap Terraform state. | | -| [bu_business_intelligence](outputs.tf#L27) | Business Intelligence attributes. | | -| [bu_business_intelligence_keys](outputs.tf#L37) | Business Intelligence service account keys. | ✓ | -| [bu_machine_learning](outputs.tf#L43) | Machine Learning attributes. | | -| [bu_machine_learning_keys](outputs.tf#L53) | Machine Learning service account keys. | ✓ | -| [shared_folder_id](outputs.tf#L59) | Shared folder id. | | -| [shared_resources_project](outputs.tf#L64) | Project that holdes resources shared across business units. | | -| [terraform_project](outputs.tf#L69) | Project that holds the base Terraform resources. | | - - diff --git a/blueprints/foundations/business-units/backend.tf.sample b/blueprints/foundations/business-units/backend.tf.sample deleted file mode 100644 index 065cd071..00000000 --- a/blueprints/foundations/business-units/backend.tf.sample +++ /dev/null @@ -1,22 +0,0 @@ -# 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 -# -# 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. - -terraform { - backend "gcs" { - # once initial apply has completed, copy this file to `backend.tf` then - # set the `bucket` value to the `bootstrap_tf_gcs_bucket` output, then - # run apply again to transfer state - bucket = "" - } -} diff --git a/blueprints/foundations/business-units/diagram.png b/blueprints/foundations/business-units/diagram.png deleted file mode 100644 index 5692b1d4acd6a669a99ac9d47659ab70bb17cb96..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64603 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 ["roles/owner"] - } - services = var.project_services -} - -# Bootstrap Terraform state GCS bucket - -module "tf-gcs-bootstrap" { - source = "../../../modules/gcs" - project_id = module.tf-project.project_id - name = "tf-bootstrap" - prefix = "${var.prefix}-tf" - location = var.gcs_defaults.location -} - -############################################################################### -# Business units # -############################################################################### - -module "bu-business-intelligence" { - source = "../../../modules/folders-unit" - name = "Business Intelligence" - short_name = "bi" - automation_project_id = module.tf-project.project_id - billing_account_id = var.billing_account_id - environments = var.environments - gcs_defaults = var.gcs_defaults - organization_id = var.organization_id - root_node = var.root_node - prefix = var.prefix - # extra variables from the folders-unit module can be used here to grant - # IAM roles to the bu users, configure the automation service accounts, etc. - # iam_roles = ["viewer"] - # iam_members = { viewer = ["user:user@example.com"] } -} - -module "bu-machine-learning" { - source = "../../../modules/folders-unit" - name = "Machine Learning" - short_name = "ml" - automation_project_id = module.tf-project.project_id - billing_account_id = var.billing_account_id - environments = var.environments - gcs_defaults = var.gcs_defaults - organization_id = var.organization_id - root_node = var.root_node - prefix = var.prefix - # extra variables from the folders-unit module can be used here to grant - # IAM roles to the bu users, configure the automation service accounts, etc. -} - -############################################################################### -# Audit log exports # -############################################################################### - -# Audit logs project - -module "audit-project" { - source = "../../../modules/project" - name = "audit" - parent = module.shared-folder.id - prefix = var.prefix - billing_account = var.billing_account_id - iam = { - "roles/viewer" = var.iam_audit_viewers - } - services = concat(var.project_services, [ - "bigquery.googleapis.com", - ]) -} - -# audit logs dataset and sink - -module "audit-dataset" { - source = "../../../modules/bigquery-dataset" - project_id = module.audit-project.project_id - id = "audit_export" - friendly_name = "Audit logs export." - # disable delete on destroy for actual use - options = { - default_table_expiration_ms = null - default_partition_expiration_ms = null - delete_contents_on_destroy = true - } -} - -# uncomment the next two modules to create the logging sinks - -# module "root_org" { -# count = local.root_node_type == "organizations" ? 1 : 0 -# source = "../../../modules/organization" -# organization_id = var.root_node -# logging_sinks = local.logging_sinks -# exclusions = {} -# } - -# module "root_folder" { -# count = local.root_node_type == "folders" ? 1 : 0 -# source = "../../../modules/folder" -# id = var.root_node -# folder_create = false -# logging_sinks = local.logging_sinks -# exclusions = {} -# } - -############################################################################### -# Shared resources (GCR, GCS, KMS, etc.) # -############################################################################### - -# Shared resources project - -module "shared-project" { - source = "../../../modules/project" - name = "shared" - parent = module.shared-folder.id - prefix = var.prefix - billing_account = var.billing_account_id - iam_additive = { - for name in var.iam_shared_owners : (name) => ["roles/owner"] - } - services = var.project_services -} - -# Add further modules here for resources that are common to all business units -# like GCS buckets (used to hold shared assets), Container Registry, KMS, etc. diff --git a/blueprints/foundations/business-units/outputs.tf b/blueprints/foundations/business-units/outputs.tf deleted file mode 100644 index 4456123a..00000000 --- a/blueprints/foundations/business-units/outputs.tf +++ /dev/null @@ -1,75 +0,0 @@ -/** - * 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. - */ - -output "audit_logs_project" { - description = "Project that holds the audit logs export resources." - value = module.audit-project.project_id -} - -output "bootstrap_tf_gcs_bucket" { - description = "GCS bucket used for the bootstrap Terraform state." - value = module.tf-gcs-bootstrap.name -} - -output "bu_business_intelligence" { - description = "Business Intelligence attributes." - value = { - unit_folder = module.bu-business-intelligence.unit_folder, - env_gcs_buckets = module.bu-business-intelligence.env_gcs_buckets - env_folders = module.bu-business-intelligence.env_folders - env_service_accounts = module.bu-business-intelligence.env_service_accounts - } -} - -output "bu_business_intelligence_keys" { - description = "Business Intelligence service account keys." - sensitive = true - value = module.bu-business-intelligence.env_sa_keys -} - -output "bu_machine_learning" { - description = "Machine Learning attributes." - value = { - unit_folder = module.bu-machine-learning.unit_folder, - env_gcs_buckets = module.bu-machine-learning.env_gcs_buckets - env_folders = module.bu-machine-learning.env_folders - env_service_accounts = module.bu-machine-learning.env_service_accounts - } -} - -output "bu_machine_learning_keys" { - description = "Machine Learning service account keys." - sensitive = true - value = module.bu-machine-learning.env_sa_keys -} - -output "shared_folder_id" { - description = "Shared folder id." - value = module.shared-folder.id -} - -output "shared_resources_project" { - description = "Project that holdes resources shared across business units." - value = module.shared-project.project_id -} - -output "terraform_project" { - description = "Project that holds the base Terraform resources." - value = module.tf-project.project_id -} - -# Add further outputs here for the additional modules that manage shared -# resources, like GCR, GCS buckets, KMS, etc. diff --git a/blueprints/foundations/business-units/terraform.tfvars.sample b/blueprints/foundations/business-units/terraform.tfvars.sample deleted file mode 100644 index 7036b969..00000000 --- a/blueprints/foundations/business-units/terraform.tfvars.sample +++ /dev/null @@ -1,19 +0,0 @@ -# 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 -# -# 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. - -billing_account_id = "014617-19UCBC-AF02D9" -organization_id= "500001140800" -prefix = "xyz" -root_node = "folders/9572793983696" -generate_keys = true diff --git a/blueprints/foundations/business-units/variables.tf b/blueprints/foundations/business-units/variables.tf deleted file mode 100644 index 69f5ac3b..00000000 --- a/blueprints/foundations/business-units/variables.tf +++ /dev/null @@ -1,91 +0,0 @@ -/** - * 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. - */ - -variable "audit_filter" { - description = "Audit log filter used for the log sink." - type = string - default = < - -## Variables - -| name | description | type | required | default | -|---|---|:---:|:---:|:---:| -| [billing_account_id](variables.tf#L25) | Billing account id used as to create projects. | string | ✓ | | -| [environments](variables.tf#L30) | Environment short names. | set(string) | ✓ | | -| [organization_id](variables.tf#L94) | Organization id in organizations/nnnnnnnn format. | string | ✓ | | -| [prefix](variables.tf#L99) | Prefix used for resources that need unique names. | string | ✓ | | -| [root_node](variables.tf#L113) | Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | | -| [audit_filter](variables.tf#L15) | Audit log filter used for the log sink. | string | | | -| [gcs_location](variables.tf#L35) | GCS bucket location. | string | | "EU" | -| [iam_audit_viewers](variables.tf#L41) | Audit project viewers, in IAM format. | list(string) | | [] | -| [iam_billing_config](variables.tf#L47) | Control granting billing user role to service accounts. Target the billing account by default. | object({…}) | | {…} | -| [iam_folder_roles](variables.tf#L59) | List of roles granted to each service account on its respective folder (excluding XPN roles). | list(string) | | […] | -| [iam_shared_owners](variables.tf#L70) | Shared services project owners, in IAM format. | list(string) | | [] | -| [iam_terraform_owners](variables.tf#L76) | Terraform project owners, in IAM format. | list(string) | | [] | -| [iam_xpn_config](variables.tf#L82) | Control granting Shared VPC creation roles to service accounts. Target the root node by default. | object({…}) | | {…} | -| [project_services](variables.tf#L104) | Service APIs enabled by default in new projects. | list(string) | | […] | -| [service_account_keys](variables.tf#L118) | Generate and store service account keys in the state file. | bool | | true | - -## Outputs - -| name | description | sensitive | -|---|---|:---:| -| [audit_logs_bq_dataset](outputs.tf#L15) | Bigquery dataset for the audit logs export. | | -| [audit_logs_project](outputs.tf#L20) | Project that holds the audit logs export resources. | | -| [bootstrap_tf_gcs_bucket](outputs.tf#L25) | GCS bucket used for the bootstrap Terraform state. | | -| [environment_folders](outputs.tf#L30) | Top-level environment folders. | | -| [environment_service_account_keys](outputs.tf#L35) | Service account keys used to run each environment Terraform modules. | ✓ | -| [environment_service_accounts](outputs.tf#L40) | Service accounts used to run each environment Terraform modules. | | -| [environment_tf_gcs_buckets](outputs.tf#L45) | GCS buckets used for each environment Terraform state. | | -| [shared_services_project](outputs.tf#L50) | Project that holdes resources shared across environments. | | -| [terraform_project](outputs.tf#L55) | Project that holds the base Terraform resources. | | - - diff --git a/blueprints/foundations/environments/backend.tf.sample b/blueprints/foundations/environments/backend.tf.sample deleted file mode 100644 index 4232a96e..00000000 --- a/blueprints/foundations/environments/backend.tf.sample +++ /dev/null @@ -1,23 +0,0 @@ -# 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 -# -# 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. - - -terraform { - backend "gcs" { - # once initial apply has completed, copy this file to `backend.tf` then - # set the `bucket` value to the `bootstrap_tf_gcs_bucket` output, then - # run apply again to transfer state - bucket = "" - } -} diff --git a/blueprints/foundations/environments/diagram.png b/blueprints/foundations/environments/diagram.png deleted file mode 100644 index ac6d79364a2490ad01572194d48d65ef971ff4ae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57325 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 S=6{f900K`}KbLh*2~7aSqXA(6 diff --git a/blueprints/foundations/environments/locals.tf b/blueprints/foundations/environments/locals.tf deleted file mode 100644 index f6c9ae0b..00000000 --- a/blueprints/foundations/environments/locals.tf +++ /dev/null @@ -1,50 +0,0 @@ -/** - * 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. - */ - -locals { - folder_roles = concat(var.iam_folder_roles, local.sa_xpn_folder_role) - organization_id = element(split("/", var.organization_id), 1) - sa_billing_account_role = ( - var.iam_billing_config.target_org ? [] : ["roles/billing.user"] - ) - sa_billing_org_role = ( - !var.iam_billing_config.target_org ? [] : ["roles/billing.user"] - ) - sa_xpn_folder_role = ( - local.sa_xpn_target_org ? [] : ["roles/compute.xpnAdmin"] - ) - sa_xpn_org_roles = ( - local.sa_xpn_target_org - ? ["roles/compute.xpnAdmin", "roles/resourcemanager.organizationViewer"] - : ["roles/resourcemanager.organizationViewer"] - ) - sa_xpn_target_org = ( - var.iam_xpn_config.target_org - || - substr(var.root_node, 0, 13) == "organizations" - ) - logging_sinks = { - audit-logs = { - type = "bigquery" - destination = module.audit-dataset.id - filter = var.audit_filter - iam = true - include_children = true - exclusions = {} - } - } - root_node_type = split("/", var.root_node)[0] -} diff --git a/blueprints/foundations/environments/main.tf b/blueprints/foundations/environments/main.tf deleted file mode 100644 index 00b62325..00000000 --- a/blueprints/foundations/environments/main.tf +++ /dev/null @@ -1,168 +0,0 @@ -# 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 -# -# 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. - -############################################################################### -# Terraform top-level resources # -############################################################################### - -# Terraform project - -module "tf-project" { - source = "../../../modules/project" - name = "terraform" - parent = var.root_node - prefix = var.prefix - billing_account = var.billing_account_id - iam_additive = { - "roles/owner" = var.iam_terraform_owners - } - services = var.project_services -} - -# per-environment service accounts - -module "tf-service-accounts" { - source = "../../../modules/iam-service-account" - for_each = var.environments - project_id = module.tf-project.project_id - name = each.value - prefix = var.prefix - iam_billing_roles = { - (var.billing_account_id) = ( - var.iam_billing_config.grant ? local.sa_billing_account_role : [] - ) - } - # folder roles are set in the folders module using authoritative bindings - iam_organization_roles = { - (local.organization_id) = concat( - var.iam_billing_config.grant ? local.sa_billing_org_role : [], - var.iam_xpn_config.grant ? local.sa_xpn_org_roles : [] - ) - } - generate_key = var.service_account_keys -} - -# bootstrap Terraform state GCS bucket - -module "tf-gcs-bootstrap" { - source = "../../../modules/gcs" - project_id = module.tf-project.project_id - name = "tf-bootstrap" - prefix = "${var.prefix}-tf" - location = var.gcs_location -} - -# per-environment Terraform state GCS buckets - -module "tf-gcs-environments" { - source = "../../../modules/gcs" - for_each = var.environments - project_id = module.tf-project.project_id - name = each.value - prefix = "${var.prefix}-tf" - location = var.gcs_location - iam = { - "roles/storage.objectAdmin" = [module.tf-service-accounts[each.value].iam_email] - } -} - -############################################################################### -# Top-level folders # -############################################################################### - -module "environment-folders" { - source = "../../../modules/folder" - for_each = var.environments - parent = var.root_node - name = each.value - iam = { - for role in local.folder_roles : - (role) => [module.tf-service-accounts[each.value].iam_email] - } -} - -############################################################################### -# Audit log exports # -############################################################################### - -# audit logs project - -module "audit-project" { - source = "../../../modules/project" - name = "audit" - parent = var.root_node - prefix = var.prefix - billing_account = var.billing_account_id - iam = { - "roles/viewer" = var.iam_audit_viewers - } - services = concat(var.project_services, [ - "bigquery.googleapis.com", - ]) -} - -# audit logs dataset and sink - -module "audit-dataset" { - source = "../../../modules/bigquery-dataset" - project_id = module.audit-project.project_id - id = "audit_export" - friendly_name = "Audit logs export." - # disable delete on destroy for actual use - options = { - default_table_expiration_ms = null - default_partition_expiration_ms = null - delete_contents_on_destroy = true - } -} - -# uncomment the next two modules to create the logging sinks - -# module "root_org" { -# count = local.root_node_type == "organizations" ? 1 : 0 -# source = "../../../modules/organization" -# organization_id = var.root_node -# logging_sinks = local.logging_sinks -# } - -# module "root_folder" { -# count = local.root_node_type == "folders" ? 1 : 0 -# source = "../../../modules/folder" -# id = var.root_node -# folder_create = false -# logging_sinks = local.logging_sinks -# } - - -############################################################################### -# Shared resources (GCR, GCS, KMS, etc.) # -############################################################################### - -# shared resources project -# see the README file for additional options on managing shared services - -module "sharedsvc-project" { - source = "../../../modules/project" - name = "sharedsvc" - parent = var.root_node - prefix = var.prefix - billing_account = var.billing_account_id - iam_additive = { - "roles/owner" = var.iam_shared_owners - } - services = var.project_services -} - -# Add further modules here for resources that are common to all environments -# like GCS buckets (used to hold shared assets), Container Registry, KMS, etc. diff --git a/blueprints/foundations/environments/outputs.tf b/blueprints/foundations/environments/outputs.tf deleted file mode 100644 index 4d5f9d4c..00000000 --- a/blueprints/foundations/environments/outputs.tf +++ /dev/null @@ -1,61 +0,0 @@ -# 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 -# -# 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. - -output "audit_logs_bq_dataset" { - description = "Bigquery dataset for the audit logs export." - value = module.audit-dataset.id -} - -output "audit_logs_project" { - description = "Project that holds the audit logs export resources." - value = module.audit-project.project_id -} - -output "bootstrap_tf_gcs_bucket" { - description = "GCS bucket used for the bootstrap Terraform state." - value = module.tf-gcs-bootstrap.name -} - -output "environment_folders" { - description = "Top-level environment folders." - value = { for folder in module.environment-folders : folder.name => folder.id } -} - -output "environment_service_account_keys" { - description = "Service account keys used to run each environment Terraform modules." - sensitive = true - value = { for env, sa in module.tf-service-accounts : env => sa.key } -} -output "environment_service_accounts" { - description = "Service accounts used to run each environment Terraform modules." - value = { for env, sa in module.tf-service-accounts : env => sa.email } -} - -output "environment_tf_gcs_buckets" { - description = "GCS buckets used for each environment Terraform state." - value = { for env, bucket in module.tf-gcs-environments : env => bucket.name } -} - -output "shared_services_project" { - description = "Project that holdes resources shared across environments." - value = module.sharedsvc-project.project_id -} - -output "terraform_project" { - description = "Project that holds the base Terraform resources." - value = module.tf-project.project_id -} - -# Add further outputs here for the additional modules that manage shared -# resources, like GCR, GCS buckets, KMS, etc. diff --git a/blueprints/foundations/environments/variables.tf b/blueprints/foundations/environments/variables.tf deleted file mode 100644 index 3b38a8ca..00000000 --- a/blueprints/foundations/environments/variables.tf +++ /dev/null @@ -1,122 +0,0 @@ -# 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 -# -# 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. - -variable "audit_filter" { - description = "Audit log filter used for the log sink." - type = string - default = < Date: Mon, 12 Sep 2022 10:15:47 +0200 Subject: [PATCH 06/16] Update readmes --- README.md | 2 +- blueprints/README.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 2eed5d89..74e82a37 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,4 @@ For more information and usage examples see each module's README file. ## End-to-end blueprints -The [blueprints](./blueprints/) in this repository are split in several main sections: **[foundational blueprints](./blueprints/foundations/)** that bootstrap the organizational hierarchy and automation prerequisites, **[networking blueprints](./blueprints/networking/)** that implement core patterns or features, **[data solutions blueprints](./blueprints/data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./blueprints/cloud-operations/)** that leverage specific products to meet specific operational needs and **[factories](./blueprints/factories/)** that implement resource factories for the repetitive creation of specific resources. +The [blueprints](./blueprints/) in this repository are split in several main sections: **[networking blueprints](./blueprints/networking/)** that implement core patterns or features, **[data solutions blueprints](./blueprints/data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./blueprints/cloud-operations/)** that leverage specific products to meet specific operational needs and **[factories](./blueprints/factories/)** that implement resource factories for the repetitive creation of specific resources, and finally [GKE](./blueprints/gke) and [serverless](./blueprints/serverless) design blueprints. diff --git a/blueprints/README.md b/blueprints/README.md index d75ef693..e06ef9ab 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -7,7 +7,9 @@ Currently available blueprints: - **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](./cloud-operations/asset-inventory-feed-remediation), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Granular Cloud DNS IAM for Shared VPC](./cloud-operations/dns-shared-vpc), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Packer image builder](./cloud-operations/packer-image-builder), [On-prem SA key management](./cloud-operations/onprem-sa-key-management), [TCP healthcheck for unmanaged GCE instances](./cloud-operations/unmanaged-instances-healthcheck), [HTTP Load Balancer with Cloud Armor](./cloud-operations/glb_and_armor) - **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/gcs-to-bq-with-least-privileges/), [Cloud Storage to Bigquery with Cloud Dataflow with least privileges](./data-solutions/gcs-to-bq-with-least-privileges/), [Data Platform Foundations](./data-solutions/data-platform-foundations/), [SQL Server AlwaysOn availability groups blueprint](./data-solutions/sqlserver-alwayson), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion/) - **factories** - [The why and the how of resource factories](./factories/README.md) +- **GKE** - [GKE multitenant fleet](./gke/multitenant-fleet/), [Shared VPC with GKE support](./networking/shared-vpc-gke/) - **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop), [PSC for on-premises Cloud Function invocation](./networking/private-cloud-function-from-onprem/), [decentralized firewall](./networking/decentralized-firewall) +- **serverless** - [Multi-region deployments for API Gateway](./serverless/api-gateway/) - **third party solutions** - [OpenShift cluster on Shared VPC](./third-party-solutions/openshift) For more information see the README files in the [foundations](./foundations/), [networking](./networking/), [data solutions](./data-solutions/), [cloud operations](./cloud-operations/) and [factories](./factories/) folders. From 96257871a2771e27212e313f57d4989943d5c677 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 12 Sep 2022 10:21:56 +0200 Subject: [PATCH 07/16] Move binautz to gke folder --- README.md | 2 +- blueprints/README.md | 2 +- blueprints/gke/README.md | 5 +++++ .../{cloud-operations => gke}/binauthz/README.md | 2 +- .../binauthz/app/clobuild.yaml | 0 .../{cloud-operations => gke}/binauthz/diagram.png | Bin .../binauthz/image/.dockerignore | 0 .../binauthz/image/.gitignore | 0 .../binauthz/image/Dockerfile | 0 .../binauthz/image/README.md | 0 .../binauthz/image/cloudbuild.yaml | 0 .../binauthz/image/index.js | 0 .../binauthz/image/package-lock.json | 0 .../binauthz/image/package.json | 0 .../{cloud-operations => gke}/binauthz/main.tf | 0 .../{cloud-operations => gke}/binauthz/outputs.tf | 0 .../binauthz/templates/app.yaml.tpl | 0 .../binauthz/templates/tenant-setup.yaml.tpl | 0 .../{cloud-operations => gke}/binauthz/variables.tf | 0 19 files changed, 8 insertions(+), 3 deletions(-) rename blueprints/{cloud-operations => gke}/binauthz/README.md (99%) rename blueprints/{cloud-operations => gke}/binauthz/app/clobuild.yaml (100%) rename blueprints/{cloud-operations => gke}/binauthz/diagram.png (100%) rename blueprints/{cloud-operations => gke}/binauthz/image/.dockerignore (100%) rename blueprints/{cloud-operations => gke}/binauthz/image/.gitignore (100%) rename blueprints/{cloud-operations => gke}/binauthz/image/Dockerfile (100%) rename blueprints/{cloud-operations => gke}/binauthz/image/README.md (100%) rename blueprints/{cloud-operations => gke}/binauthz/image/cloudbuild.yaml (100%) rename blueprints/{cloud-operations => gke}/binauthz/image/index.js (100%) rename blueprints/{cloud-operations => gke}/binauthz/image/package-lock.json (100%) rename blueprints/{cloud-operations => gke}/binauthz/image/package.json (100%) rename blueprints/{cloud-operations => gke}/binauthz/main.tf (100%) rename blueprints/{cloud-operations => gke}/binauthz/outputs.tf (100%) rename blueprints/{cloud-operations => gke}/binauthz/templates/app.yaml.tpl (100%) rename blueprints/{cloud-operations => gke}/binauthz/templates/tenant-setup.yaml.tpl (100%) rename blueprints/{cloud-operations => gke}/binauthz/variables.tf (100%) diff --git a/README.md b/README.md index 74e82a37..b4711da7 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,4 @@ For more information and usage examples see each module's README file. ## End-to-end blueprints -The [blueprints](./blueprints/) in this repository are split in several main sections: **[networking blueprints](./blueprints/networking/)** that implement core patterns or features, **[data solutions blueprints](./blueprints/data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./blueprints/cloud-operations/)** that leverage specific products to meet specific operational needs and **[factories](./blueprints/factories/)** that implement resource factories for the repetitive creation of specific resources, and finally [GKE](./blueprints/gke) and [serverless](./blueprints/serverless) design blueprints. +The [blueprints](./blueprints/) in this repository are split in several main sections: **[networking blueprints](./blueprints/networking/)** that implement core patterns or features, **[data solutions blueprints](./blueprints/data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./blueprints/cloud-operations/)** that leverage specific products to meet specific operational needs and **[factories](./blueprints/factories/)** that implement resource factories for the repetitive creation of specific resources, and finally **[GKE](./blueprints/gke)** and **[serverless](./blueprints/serverless)** design blueprints. diff --git a/blueprints/README.md b/blueprints/README.md index e06ef9ab..9fa17cbc 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -7,7 +7,7 @@ Currently available blueprints: - **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](./cloud-operations/asset-inventory-feed-remediation), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Granular Cloud DNS IAM for Shared VPC](./cloud-operations/dns-shared-vpc), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Packer image builder](./cloud-operations/packer-image-builder), [On-prem SA key management](./cloud-operations/onprem-sa-key-management), [TCP healthcheck for unmanaged GCE instances](./cloud-operations/unmanaged-instances-healthcheck), [HTTP Load Balancer with Cloud Armor](./cloud-operations/glb_and_armor) - **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/gcs-to-bq-with-least-privileges/), [Cloud Storage to Bigquery with Cloud Dataflow with least privileges](./data-solutions/gcs-to-bq-with-least-privileges/), [Data Platform Foundations](./data-solutions/data-platform-foundations/), [SQL Server AlwaysOn availability groups blueprint](./data-solutions/sqlserver-alwayson), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion/) - **factories** - [The why and the how of resource factories](./factories/README.md) -- **GKE** - [GKE multitenant fleet](./gke/multitenant-fleet/), [Shared VPC with GKE support](./networking/shared-vpc-gke/) +- **GKE** - [GKE multitenant fleet](./gke/multitenant-fleet/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [Binary Authorization Pipeline](./gke/binauthz/) - **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop), [PSC for on-premises Cloud Function invocation](./networking/private-cloud-function-from-onprem/), [decentralized firewall](./networking/decentralized-firewall) - **serverless** - [Multi-region deployments for API Gateway](./serverless/api-gateway/) - **third party solutions** - [OpenShift cluster on Shared VPC](./third-party-solutions/openshift) diff --git a/blueprints/gke/README.md b/blueprints/gke/README.md index 307c9e23..688d8f54 100644 --- a/blueprints/gke/README.md +++ b/blueprints/gke/README.md @@ -17,3 +17,8 @@ They are meant to be used as minimal but complete starting points to create actu It is meant to be used as a starting point for most Shared VPC configurations, and to be integrated to the above blueprints where Shared VPC is needed in more complex network topologies.
+ +### Binary Authorization Pipeline + +
This [blueprint](../gke/binauthz/) shows how to create a CI and a CD pipeline in Cloud Build for the deployment of an application to a private GKE cluster with unrestricted access to a public endpoint. The blueprint enables a Binary Authorization policy in the project so only images that have been attested can be deployed to the cluster. The attestations are created using a cryptographic key pair that has been provisioned in KMS. +
diff --git a/blueprints/cloud-operations/binauthz/README.md b/blueprints/gke/binauthz/README.md similarity index 99% rename from blueprints/cloud-operations/binauthz/README.md rename to blueprints/gke/binauthz/README.md index 5197e6e2..dad6dd8a 100644 --- a/blueprints/cloud-operations/binauthz/README.md +++ b/blueprints/gke/binauthz/README.md @@ -1,4 +1,4 @@ -# Binary Authorization +# Binary Authorization Pipeline Blueprint The following blueprint shows to how to create a CI and a CD pipeline in Cloud Build for the deployment of an application to a private GKE cluster with unrestricted access to a public endpoint. The blueprint enables a Binary Authorization policy in the project so only images that have been attested can be deployed to the cluster. The attestations are created using a cryptographic key pair that has been provisioned in KMS. diff --git a/blueprints/cloud-operations/binauthz/app/clobuild.yaml b/blueprints/gke/binauthz/app/clobuild.yaml similarity index 100% rename from blueprints/cloud-operations/binauthz/app/clobuild.yaml rename to blueprints/gke/binauthz/app/clobuild.yaml diff --git a/blueprints/cloud-operations/binauthz/diagram.png b/blueprints/gke/binauthz/diagram.png similarity index 100% rename from blueprints/cloud-operations/binauthz/diagram.png rename to blueprints/gke/binauthz/diagram.png diff --git a/blueprints/cloud-operations/binauthz/image/.dockerignore b/blueprints/gke/binauthz/image/.dockerignore similarity index 100% rename from blueprints/cloud-operations/binauthz/image/.dockerignore rename to blueprints/gke/binauthz/image/.dockerignore diff --git a/blueprints/cloud-operations/binauthz/image/.gitignore b/blueprints/gke/binauthz/image/.gitignore similarity index 100% rename from blueprints/cloud-operations/binauthz/image/.gitignore rename to blueprints/gke/binauthz/image/.gitignore diff --git a/blueprints/cloud-operations/binauthz/image/Dockerfile b/blueprints/gke/binauthz/image/Dockerfile similarity index 100% rename from blueprints/cloud-operations/binauthz/image/Dockerfile rename to blueprints/gke/binauthz/image/Dockerfile diff --git a/blueprints/cloud-operations/binauthz/image/README.md b/blueprints/gke/binauthz/image/README.md similarity index 100% rename from blueprints/cloud-operations/binauthz/image/README.md rename to blueprints/gke/binauthz/image/README.md diff --git a/blueprints/cloud-operations/binauthz/image/cloudbuild.yaml b/blueprints/gke/binauthz/image/cloudbuild.yaml similarity index 100% rename from blueprints/cloud-operations/binauthz/image/cloudbuild.yaml rename to blueprints/gke/binauthz/image/cloudbuild.yaml diff --git a/blueprints/cloud-operations/binauthz/image/index.js b/blueprints/gke/binauthz/image/index.js similarity index 100% rename from blueprints/cloud-operations/binauthz/image/index.js rename to blueprints/gke/binauthz/image/index.js diff --git a/blueprints/cloud-operations/binauthz/image/package-lock.json b/blueprints/gke/binauthz/image/package-lock.json similarity index 100% rename from blueprints/cloud-operations/binauthz/image/package-lock.json rename to blueprints/gke/binauthz/image/package-lock.json diff --git a/blueprints/cloud-operations/binauthz/image/package.json b/blueprints/gke/binauthz/image/package.json similarity index 100% rename from blueprints/cloud-operations/binauthz/image/package.json rename to blueprints/gke/binauthz/image/package.json diff --git a/blueprints/cloud-operations/binauthz/main.tf b/blueprints/gke/binauthz/main.tf similarity index 100% rename from blueprints/cloud-operations/binauthz/main.tf rename to blueprints/gke/binauthz/main.tf diff --git a/blueprints/cloud-operations/binauthz/outputs.tf b/blueprints/gke/binauthz/outputs.tf similarity index 100% rename from blueprints/cloud-operations/binauthz/outputs.tf rename to blueprints/gke/binauthz/outputs.tf diff --git a/blueprints/cloud-operations/binauthz/templates/app.yaml.tpl b/blueprints/gke/binauthz/templates/app.yaml.tpl similarity index 100% rename from blueprints/cloud-operations/binauthz/templates/app.yaml.tpl rename to blueprints/gke/binauthz/templates/app.yaml.tpl diff --git a/blueprints/cloud-operations/binauthz/templates/tenant-setup.yaml.tpl b/blueprints/gke/binauthz/templates/tenant-setup.yaml.tpl similarity index 100% rename from blueprints/cloud-operations/binauthz/templates/tenant-setup.yaml.tpl rename to blueprints/gke/binauthz/templates/tenant-setup.yaml.tpl diff --git a/blueprints/cloud-operations/binauthz/variables.tf b/blueprints/gke/binauthz/variables.tf similarity index 100% rename from blueprints/cloud-operations/binauthz/variables.tf rename to blueprints/gke/binauthz/variables.tf From 96edc4b58f962389e851ad3e72fe58f62646e1a4 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Mon, 12 Sep 2022 10:26:33 +0200 Subject: [PATCH 08/16] Move gke multi cluster mesh example to gke folder --- blueprints/README.md | 2 +- blueprints/gke/README.md | 5 +++++ .../multi-cluster-mesh-gke-fleet-api/.gitignore | 0 .../multi-cluster-mesh-gke-fleet-api/README.md | 2 +- .../ansible/ansible.cfg | 0 .../ansible/inventory/hosts.ini | 0 .../ansible/playbook.yaml | 0 .../install/tasks/endpoint-discovery-config.yaml | 0 .../ansible/roles/install/tasks/install.yaml | 0 .../ansible/roles/install/tasks/main.yaml | 0 .../ansible/roles/prerequisites/tasks/main.yaml | 0 .../ansible/roles/test/tasks/main.yaml | 0 .../ansible/roles/test/tasks/test.yaml | 0 .../multi-cluster-mesh-gke-fleet-api/diagram.png} | Bin .../multi-cluster-mesh-gke-fleet-api/main.tf | 0 .../templates/gssh.sh.tpl | 0 .../templates/vars.yaml.tpl | 0 .../multi-cluster-mesh-gke-fleet-api/variables.tf | 0 18 files changed, 7 insertions(+), 2 deletions(-) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/.gitignore (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/README.md (99%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/ansible.cfg (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/inventory/hosts.ini (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/playbook.yaml (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/endpoint-discovery-config.yaml (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/install.yaml (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/main.yaml (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/roles/prerequisites/tasks/main.yaml (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/main.yaml (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/test.yaml (100%) rename blueprints/{cloud-operations/multi-cluster-mesh-gke-fleet-api/architecture.png => gke/multi-cluster-mesh-gke-fleet-api/diagram.png} (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/main.tf (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/templates/gssh.sh.tpl (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/templates/vars.yaml.tpl (100%) rename blueprints/{cloud-operations => gke}/multi-cluster-mesh-gke-fleet-api/variables.tf (100%) diff --git a/blueprints/README.md b/blueprints/README.md index 9fa17cbc..45bc2dc9 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -7,7 +7,7 @@ Currently available blueprints: - **cloud operations** - [Resource tracking and remediation via Cloud Asset feeds](./cloud-operations/asset-inventory-feed-remediation), [Granular Cloud DNS IAM via Service Directory](./cloud-operations/dns-fine-grained-iam), [Granular Cloud DNS IAM for Shared VPC](./cloud-operations/dns-shared-vpc), [Compute Engine quota monitoring](./cloud-operations/quota-monitoring), [Scheduled Cloud Asset Inventory Export to Bigquery](./cloud-operations/scheduled-asset-inventory-export-bq), [Packer image builder](./cloud-operations/packer-image-builder), [On-prem SA key management](./cloud-operations/onprem-sa-key-management), [TCP healthcheck for unmanaged GCE instances](./cloud-operations/unmanaged-instances-healthcheck), [HTTP Load Balancer with Cloud Armor](./cloud-operations/glb_and_armor) - **data solutions** - [GCE/GCS CMEK via centralized Cloud KMS](./data-solutions/gcs-to-bq-with-least-privileges/), [Cloud Storage to Bigquery with Cloud Dataflow with least privileges](./data-solutions/gcs-to-bq-with-least-privileges/), [Data Platform Foundations](./data-solutions/data-platform-foundations/), [SQL Server AlwaysOn availability groups blueprint](./data-solutions/sqlserver-alwayson), [Cloud SQL instance with multi-region read replicas](./data-solutions/cloudsql-multiregion/) - **factories** - [The why and the how of resource factories](./factories/README.md) -- **GKE** - [GKE multitenant fleet](./gke/multitenant-fleet/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [Binary Authorization Pipeline](./gke/binauthz/) +- **GKE** - [GKE multitenant fleet](./gke/multitenant-fleet/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [Binary Authorization Pipeline](./gke/binauthz/), [Multi-cluster mesh on GKE (fleet API)](./gke/multi-cluster-mesh-gke-fleet-api/) - **networking** - [hub and spoke via peering](./networking/hub-and-spoke-peering/), [hub and spoke via VPN](./networking/hub-and-spoke-vpn/), [DNS and Google Private Access for on-premises](./networking/onprem-google-access-dns/), [Shared VPC with GKE support](./networking/shared-vpc-gke/), [ILB as next hop](./networking/ilb-next-hop), [PSC for on-premises Cloud Function invocation](./networking/private-cloud-function-from-onprem/), [decentralized firewall](./networking/decentralized-firewall) - **serverless** - [Multi-region deployments for API Gateway](./serverless/api-gateway/) - **third party solutions** - [OpenShift cluster on Shared VPC](./third-party-solutions/openshift) diff --git a/blueprints/gke/README.md b/blueprints/gke/README.md index 688d8f54..a2c48071 100644 --- a/blueprints/gke/README.md +++ b/blueprints/gke/README.md @@ -22,3 +22,8 @@ It is meant to be used as a starting point for most Shared VPC configurations, a This [blueprint](../gke/binauthz/) shows how to create a CI and a CD pipeline in Cloud Build for the deployment of an application to a private GKE cluster with unrestricted access to a public endpoint. The blueprint enables a Binary Authorization policy in the project so only images that have been attested can be deployed to the cluster. The attestations are created using a cryptographic key pair that has been provisioned in KMS.
+ +### Multi-cluster mesh on GKE (fleet API) + + This [blueprint](../gke/multi-cluster-mesh-gke-fleet-api/) shows how to create a multi-cluster mesh for two private clusters on GKE. Anthos Service Mesh with automatic control plane management is set up for clusters using the Fleet API. This can only be done if the clusters are in a single project and in the same VPC. In this particular case both clusters having being deployed to different subnets in a shared VPC. +
diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/.gitignore b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/.gitignore similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/.gitignore rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/.gitignore diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/README.md b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/README.md similarity index 99% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/README.md rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/README.md index 9cac15be..484e6b13 100644 --- a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/README.md +++ b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/README.md @@ -4,7 +4,7 @@ The following blueprint shows how to create a multi-cluster mesh for two private The diagram below depicts the architecture of the blueprint. -![Architecture](architecture.png) +![Architecture diagram](diagram.png) Terraform is used to provision the required infrastructure, create the IAM binding and register the clusters to the fleet. diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/ansible.cfg b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/ansible.cfg similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/ansible.cfg rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/ansible.cfg diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/inventory/hosts.ini b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/inventory/hosts.ini similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/inventory/hosts.ini rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/inventory/hosts.ini diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/playbook.yaml b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/playbook.yaml similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/playbook.yaml rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/playbook.yaml diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/endpoint-discovery-config.yaml b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/endpoint-discovery-config.yaml similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/endpoint-discovery-config.yaml rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/endpoint-discovery-config.yaml diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/install.yaml b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/install.yaml similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/install.yaml rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/install.yaml diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/main.yaml b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/main.yaml similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/main.yaml rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/install/tasks/main.yaml diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/prerequisites/tasks/main.yaml b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/prerequisites/tasks/main.yaml similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/prerequisites/tasks/main.yaml rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/prerequisites/tasks/main.yaml diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/main.yaml b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/main.yaml similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/main.yaml rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/main.yaml diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/test.yaml b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/test.yaml similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/test.yaml rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/ansible/roles/test/tasks/test.yaml diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/architecture.png b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/diagram.png similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/architecture.png rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/diagram.png diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/main.tf b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/main.tf similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/main.tf rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/main.tf diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/templates/gssh.sh.tpl b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/templates/gssh.sh.tpl similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/templates/gssh.sh.tpl rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/templates/gssh.sh.tpl diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/templates/vars.yaml.tpl b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/templates/vars.yaml.tpl similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/templates/vars.yaml.tpl rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/templates/vars.yaml.tpl diff --git a/blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/variables.tf b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/variables.tf similarity index 100% rename from blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api/variables.tf rename to blueprints/gke/multi-cluster-mesh-gke-fleet-api/variables.tf From 91af0e8535dbc6327812585bb4ce70ddf1a0317f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Sep 2022 10:30:19 +0200 Subject: [PATCH 09/16] fix links in gke stage readmes --- fast/stages/03-gke-multitenant/README.md | 2 +- fast/stages/03-gke-multitenant/dev/README.md | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/fast/stages/03-gke-multitenant/README.md b/fast/stages/03-gke-multitenant/README.md index c420659e..f08910c8 100644 --- a/fast/stages/03-gke-multitenant/README.md +++ b/fast/stages/03-gke-multitenant/README.md @@ -6,4 +6,4 @@ The Terraform code follows the same general approach used for the [project facto The [`dev` folder](./dev/) contains an example setup for a generic development environment, and can be used as-is or cloned to implement other environments, or more specialized setups -Refer to [the `dev` documentation](./dev/README.md) configuration details, and to [the `gke-serverless` documentation](../../../blueprints/gke-serverless/multitenant-fleet) for the architectural design and decisions taken. +Refer to [the `dev` documentation](./dev/README.md) configuration details, and to [the `gke-serverless` documentation](../../../blueprints/gke/multitenant-fleet) for the architectural design and decisions taken. diff --git a/fast/stages/03-gke-multitenant/dev/README.md b/fast/stages/03-gke-multitenant/dev/README.md index af46cade..9221facf 100644 --- a/fast/stages/03-gke-multitenant/dev/README.md +++ b/fast/stages/03-gke-multitenant/dev/README.md @@ -10,7 +10,7 @@ The following diagram illustrates the high-level design of created resources, wh ## Design overview and choices -> The detailed architecture of the underlying resources is explained in the documentation of [GKE multitenant module](../../../../blueprints/gke-serverless/multitenant-fleet/README.md). +> The detailed architecture of the underlying resources is explained in the documentation of [GKE multitenant module](../../../../blueprints/gke/multitenant-fleet/README.md). This stage creates a project containing and as many clusters and node pools as requested by the user through the [variables](#variables) explained below. The GKE clusters are created with the with the following setup: @@ -37,7 +37,6 @@ This stage creates a project containing and as many clusters and node pools as r - [Use of the GCE persistent disk CSI driver](https://cloud.google.com/kubernetes-engine/docs/how-to/persistent-volumes/gce-pd-csi-driver) - Node [auto-upgrade](https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-upgrades) and [auto-repair](https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-repair) for all node pools - ## How to run this stage This stage is meant to be executed after "foundational stages" (i.e., stages [`00-bootstrap`](../../00-bootstrap), [`01-resman`](../../01-resman), 02-networking (either [VPN](../../02-networking-vpn) or [NVA](../../02-networking-nva)) and [`02-security`](../../02-security)) have been run. From a2849cf4a0cf68c6ca949889149d48d65dbffead Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Sep 2022 10:33:04 +0200 Subject: [PATCH 10/16] remove stale foundation blueprints links --- blueprints/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/README.md b/blueprints/README.md index 45bc2dc9..046e03a9 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -1,6 +1,6 @@ # Terraform end-to-end blueprints for Google Cloud -This section contains **[foundational blueprints](./foundations/)** that bootstrap the organizational hierarchy and automation prerequisites, **[networking blueprints](./networking/)** that implement core patterns or features, **[data solutions blueprints](./data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./cloud-operations/)** that leverage specific products to meet specific operational needs and **[factories](./factories/)** that implement resource factories for the repetitive creation of specific resources. +This section **[networking blueprints](./networking/)** that implement core patterns or features, **[data solutions blueprints](./data-solutions/)** that demonstrate how to integrate data services in complete scenarios, **[cloud operations blueprints](./cloud-operations/)** that leverage specific products to meet specific operational needs, **[GKE](./gke/)** and **[Serverless](./serverless/)** blueprints, and **[factories](./factories/)** that implement resource factories for the repetitive creation of specific resources. Currently available blueprints: From 8026eef3b7f78439c6fb30c38ee4e47fb143f62c Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Sep 2022 11:06:01 +0200 Subject: [PATCH 11/16] remove stale link in blueprints readme --- blueprints/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blueprints/README.md b/blueprints/README.md index 046e03a9..b38370bb 100644 --- a/blueprints/README.md +++ b/blueprints/README.md @@ -12,4 +12,4 @@ Currently available blueprints: - **serverless** - [Multi-region deployments for API Gateway](./serverless/api-gateway/) - **third party solutions** - [OpenShift cluster on Shared VPC](./third-party-solutions/openshift) -For more information see the README files in the [foundations](./foundations/), [networking](./networking/), [data solutions](./data-solutions/), [cloud operations](./cloud-operations/) and [factories](./factories/) folders. +For more information see the individual README files in each section. From a1a78847f4011f4358db03b8d0e00bfd370e67d6 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Sep 2022 11:08:37 +0200 Subject: [PATCH 12/16] try top-level symlink --- examples | 1 + 1 file changed, 1 insertion(+) create mode 120000 examples diff --git a/examples b/examples new file mode 120000 index 00000000..6d28d90e --- /dev/null +++ b/examples @@ -0,0 +1 @@ +blueprints \ No newline at end of file From 02ff697fd7fa01bcf2094a49955734d95f0500aa Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Sep 2022 11:09:36 +0200 Subject: [PATCH 13/16] remove symlink --- examples | 1 - 1 file changed, 1 deletion(-) delete mode 120000 examples diff --git a/examples b/examples deleted file mode 120000 index 6d28d90e..00000000 --- a/examples +++ /dev/null @@ -1 +0,0 @@ -blueprints \ No newline at end of file From aea24f24f87f688f402556a643d2d652a1c76fed Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Sep 2022 11:54:18 +0200 Subject: [PATCH 14/16] fix blueprint tests --- blueprints/gke/multitenant-fleet/README.md | 7 +- tests/blueprints/foundations/__init__.py | 13 ---- .../foundations/business_units/__init__.py | 13 ---- .../business_units/fixture/main.tf | 23 ------- .../business_units/fixture/variables.tf | 35 ---------- .../foundations/business_units/test_plan.py | 19 ------ .../foundations/environments/__init__.py | 13 ---- .../foundations/environments/fixture/main.tf | 28 -------- .../environments/fixture/variables.tf | 66 ------------------- .../foundations/environments/test_plan.py | 51 -------------- tests/blueprints/gke/__init__.py | 0 .../binauthz/__init__.py | 0 .../binauthz/fixture/main.tf | 2 +- .../binauthz/fixture/variables.tf | 0 .../binauthz/test_plan.py | 0 .../__init__.py | 0 .../fixture/main.tf | 2 +- .../fixture/variables.tf | 0 .../test_plan.py | 0 19 files changed, 5 insertions(+), 267 deletions(-) delete mode 100644 tests/blueprints/foundations/__init__.py delete mode 100644 tests/blueprints/foundations/business_units/__init__.py delete mode 100644 tests/blueprints/foundations/business_units/fixture/main.tf delete mode 100644 tests/blueprints/foundations/business_units/fixture/variables.tf delete mode 100644 tests/blueprints/foundations/business_units/test_plan.py delete mode 100644 tests/blueprints/foundations/environments/__init__.py delete mode 100644 tests/blueprints/foundations/environments/fixture/main.tf delete mode 100644 tests/blueprints/foundations/environments/fixture/variables.tf delete mode 100644 tests/blueprints/foundations/environments/test_plan.py create mode 100644 tests/blueprints/gke/__init__.py rename tests/blueprints/{cloud_operations => gke}/binauthz/__init__.py (100%) rename tests/blueprints/{cloud_operations => gke}/binauthz/fixture/main.tf (90%) rename tests/blueprints/{cloud_operations => gke}/binauthz/fixture/variables.tf (100%) rename tests/blueprints/{cloud_operations => gke}/binauthz/test_plan.py (100%) rename tests/blueprints/{cloud_operations => gke}/multi_cluster_mesh_gke_fleet_api/__init__.py (100%) rename tests/blueprints/{cloud_operations => gke}/multi_cluster_mesh_gke_fleet_api/fixture/main.tf (90%) rename tests/blueprints/{cloud_operations => gke}/multi_cluster_mesh_gke_fleet_api/fixture/variables.tf (100%) rename tests/blueprints/{cloud_operations => gke}/multi_cluster_mesh_gke_fleet_api/test_plan.py (100%) diff --git a/blueprints/gke/multitenant-fleet/README.md b/blueprints/gke/multitenant-fleet/README.md index dd6d97ff..68909b82 100644 --- a/blueprints/gke/multitenant-fleet/README.md +++ b/blueprints/gke/multitenant-fleet/README.md @@ -45,7 +45,7 @@ The following example shows how to deploy a single cluster and a single node poo ```hcl module "gke" { - source = "./fabric/blueprints/gke-serverless/multitenant-fleet/" + source = "./fabric/blueprints/gke/multitenant-fleet/" project_id = var.project_id billing_account_id = var.billing_account_id folder_id = var.folder_id @@ -106,10 +106,9 @@ The first cluster `cluster-euw1` defines the mandatory configuration parameters On the other hand, the second cluster (`cluster-euw3`) defines its own configuration by providing a value to the `overrides` key. - ```hcl module "gke" { - source = "./fabric/blueprints/gke-serverless/multitenant-fleet/" + source = "./fabric/blueprints/gke/multitenant-fleet/" project_id = var.project_id billing_account_id = var.billing_account_id folder_id = var.folder_id @@ -200,7 +199,7 @@ This example deploys two clusters and configures several GKE Fleet features: ```hcl module "gke" { - source = "./fabric/blueprints/gke-serverless/multitenant-fleet/" + source = "./fabric/blueprints/gke/multitenant-fleet/" project_id = var.project_id billing_account_id = var.billing_account_id folder_id = var.folder_id diff --git a/tests/blueprints/foundations/__init__.py b/tests/blueprints/foundations/__init__.py deleted file mode 100644 index 6d6d1266..00000000 --- a/tests/blueprints/foundations/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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. diff --git a/tests/blueprints/foundations/business_units/__init__.py b/tests/blueprints/foundations/business_units/__init__.py deleted file mode 100644 index 6d6d1266..00000000 --- a/tests/blueprints/foundations/business_units/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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. diff --git a/tests/blueprints/foundations/business_units/fixture/main.tf b/tests/blueprints/foundations/business_units/fixture/main.tf deleted file mode 100644 index 2609c1c9..00000000 --- a/tests/blueprints/foundations/business_units/fixture/main.tf +++ /dev/null @@ -1,23 +0,0 @@ -/** - * 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. - */ - -module "test" { - source = "../../../../../blueprints/foundations/business-units" - billing_account_id = var.billing_account_id - organization_id = var.organization_id - prefix = var.prefix - root_node = var.root_node -} diff --git a/tests/blueprints/foundations/business_units/fixture/variables.tf b/tests/blueprints/foundations/business_units/fixture/variables.tf deleted file mode 100644 index a8997595..00000000 --- a/tests/blueprints/foundations/business_units/fixture/variables.tf +++ /dev/null @@ -1,35 +0,0 @@ -# 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 -# -# 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. - -variable "billing_account_id" { - type = string - default = "1234-5678-9012" -} - -variable "organization_id" { - type = string - default = "organizations/1234567890" -} - -variable "prefix" { - description = "Prefix used for resources that need unique names." - type = string - default = "test" -} - -variable "root_node" { - description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." - type = string - default = "folders/1234567890" -} diff --git a/tests/blueprints/foundations/business_units/test_plan.py b/tests/blueprints/foundations/business_units/test_plan.py deleted file mode 100644 index 816d514b..00000000 --- a/tests/blueprints/foundations/business_units/test_plan.py +++ /dev/null @@ -1,19 +0,0 @@ -# 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. - -def test_resources(e2e_plan_runner): - "Test that plan works and the numbers of resources is as expected." - modules, resources = e2e_plan_runner() - assert len(modules) == 8 - assert len(resources) == 83 diff --git a/tests/blueprints/foundations/environments/__init__.py b/tests/blueprints/foundations/environments/__init__.py deleted file mode 100644 index 6d6d1266..00000000 --- a/tests/blueprints/foundations/environments/__init__.py +++ /dev/null @@ -1,13 +0,0 @@ -# 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. diff --git a/tests/blueprints/foundations/environments/fixture/main.tf b/tests/blueprints/foundations/environments/fixture/main.tf deleted file mode 100644 index 21832e9d..00000000 --- a/tests/blueprints/foundations/environments/fixture/main.tf +++ /dev/null @@ -1,28 +0,0 @@ -/** - * 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. - */ - -module "test" { - source = "../../../../../blueprints/foundations/environments" - billing_account_id = var.billing_account_id - environments = var.environments - iam_audit_viewers = var.iam_audit_viewers - iam_shared_owners = var.iam_shared_owners - iam_terraform_owners = var.iam_terraform_owners - iam_xpn_config = var.iam_xpn_config - organization_id = var.organization_id - prefix = var.prefix - root_node = var.root_node -} diff --git a/tests/blueprints/foundations/environments/fixture/variables.tf b/tests/blueprints/foundations/environments/fixture/variables.tf deleted file mode 100644 index 48ce5fde..00000000 --- a/tests/blueprints/foundations/environments/fixture/variables.tf +++ /dev/null @@ -1,66 +0,0 @@ -# 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 -# -# 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. - -variable "billing_account_id" { - type = string - default = "1234-5678-9012" -} - -variable "environments" { - type = list(string) - default = ["test", "prod"] -} - -variable "iam_audit_viewers" { - type = list(string) - default = ["user:audit-1@example.org", "user:audit2@example.org"] -} - -variable "iam_shared_owners" { - type = list(string) - default = ["user:shared-1@example.org", "user:shared-2@example.org"] -} - -variable "iam_terraform_owners" { - type = list(string) - default = ["user:tf-1@example.org", "user:tf-2@example.org"] -} - -variable "iam_xpn_config" { - type = object({ - grant = bool - target_org = bool - }) - default = { - grant = true - target_org = false - } -} - -variable "organization_id" { - type = string - default = "" -} - -variable "prefix" { - description = "Prefix used for resources that need unique names." - type = string - default = "test" -} - -variable "root_node" { - description = "Root node for the new hierarchy, either 'organizations/org_id' or 'folders/folder_id'." - type = string - default = "folders/1234567890" -} diff --git a/tests/blueprints/foundations/environments/test_plan.py b/tests/blueprints/foundations/environments/test_plan.py deleted file mode 100644 index 7e8762df..00000000 --- a/tests/blueprints/foundations/environments/test_plan.py +++ /dev/null @@ -1,51 +0,0 @@ -# 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. - - -def test_folder_roles(e2e_plan_runner): - "Test folder roles." - modules, _ = e2e_plan_runner(refresh=False) - for env in ['test', 'prod']: - resources = modules[f'module.test.module.environment-folders["{env}"]'] - folders = [r for r in resources if r['type'] == 'google_folder'] - assert len(folders) == 1 - folder = folders[0] - assert folder['values']['display_name'] == env - - bindings = [r['index'] - for r in resources if r['type'] == 'google_folder_iam_binding'] - assert len(bindings) == 5 - - -def test_org_roles(e2e_plan_runner): - "Test folder roles." - tf_vars = { - 'organization_id': 'organizations/123', - 'iam_xpn_config': '{grant = true, target_org = true}' - } - modules, _ = e2e_plan_runner(refresh=False, **tf_vars) - for env in ['test', 'prod']: - resources = modules[f'module.test.module.environment-folders["{env}"]'] - folder_bindings = [r['index'] - for r in resources if r['type'] == 'google_folder_iam_binding'] - assert len(folder_bindings) == 4 - - resources = modules[f'module.test.module.tf-service-accounts["{env}"]'] - org_bindings = [r for r in resources - if r['type'] == 'google_organization_iam_member'] - assert len(org_bindings) == 2 - assert {b['values']['role'] for b in org_bindings} == { - 'roles/resourcemanager.organizationViewer', - 'roles/compute.xpnAdmin' - } diff --git a/tests/blueprints/gke/__init__.py b/tests/blueprints/gke/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/blueprints/cloud_operations/binauthz/__init__.py b/tests/blueprints/gke/binauthz/__init__.py similarity index 100% rename from tests/blueprints/cloud_operations/binauthz/__init__.py rename to tests/blueprints/gke/binauthz/__init__.py diff --git a/tests/blueprints/cloud_operations/binauthz/fixture/main.tf b/tests/blueprints/gke/binauthz/fixture/main.tf similarity index 90% rename from tests/blueprints/cloud_operations/binauthz/fixture/main.tf rename to tests/blueprints/gke/binauthz/fixture/main.tf index c815e658..eefdacc8 100644 --- a/tests/blueprints/cloud_operations/binauthz/fixture/main.tf +++ b/tests/blueprints/gke/binauthz/fixture/main.tf @@ -15,7 +15,7 @@ */ module "test" { - source = "../../../../../blueprints/cloud-operations/binauthz" + source = "../../../../../blueprints/gke/binauthz" project_create = var.project_create project_id = var.project_id } diff --git a/tests/blueprints/cloud_operations/binauthz/fixture/variables.tf b/tests/blueprints/gke/binauthz/fixture/variables.tf similarity index 100% rename from tests/blueprints/cloud_operations/binauthz/fixture/variables.tf rename to tests/blueprints/gke/binauthz/fixture/variables.tf diff --git a/tests/blueprints/cloud_operations/binauthz/test_plan.py b/tests/blueprints/gke/binauthz/test_plan.py similarity index 100% rename from tests/blueprints/cloud_operations/binauthz/test_plan.py rename to tests/blueprints/gke/binauthz/test_plan.py diff --git a/tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/__init__.py b/tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/__init__.py similarity index 100% rename from tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/__init__.py rename to tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/__init__.py diff --git a/tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/fixture/main.tf b/tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/fixture/main.tf similarity index 90% rename from tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/fixture/main.tf rename to tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/fixture/main.tf index 3db317d9..47524fa5 100644 --- a/tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/fixture/main.tf +++ b/tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/fixture/main.tf @@ -15,7 +15,7 @@ */ module "test" { - source = "../../../../../blueprints/cloud-operations/multi-cluster-mesh-gke-fleet-api" + source = "../../../../../blueprints/gke/multi-cluster-mesh-gke-fleet-api" billing_account_id = var.billing_account_id parent = var.parent host_project_id = var.host_project_id diff --git a/tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/fixture/variables.tf b/tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/fixture/variables.tf similarity index 100% rename from tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/fixture/variables.tf rename to tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/fixture/variables.tf diff --git a/tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/test_plan.py b/tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/test_plan.py similarity index 100% rename from tests/blueprints/cloud_operations/multi_cluster_mesh_gke_fleet_api/test_plan.py rename to tests/blueprints/gke/multi_cluster_mesh_gke_fleet_api/test_plan.py From 5cdcc2a767ead7a33f79b9dc94e3a1a0cac0cdfa Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Sep 2022 11:58:00 +0200 Subject: [PATCH 15/16] add gke multitenant test --- .../gke/multitenant_fleet/__init__.py | 13 +++++ .../gke/multitenant_fleet/fixture/main.tf | 54 +++++++++++++++++++ .../gke/multitenant_fleet/test_plan.py | 20 +++++++ 3 files changed, 87 insertions(+) create mode 100644 tests/blueprints/gke/multitenant_fleet/__init__.py create mode 100644 tests/blueprints/gke/multitenant_fleet/fixture/main.tf create mode 100644 tests/blueprints/gke/multitenant_fleet/test_plan.py diff --git a/tests/blueprints/gke/multitenant_fleet/__init__.py b/tests/blueprints/gke/multitenant_fleet/__init__.py new file mode 100644 index 00000000..6d6d1266 --- /dev/null +++ b/tests/blueprints/gke/multitenant_fleet/__init__.py @@ -0,0 +1,13 @@ +# 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. diff --git a/tests/blueprints/gke/multitenant_fleet/fixture/main.tf b/tests/blueprints/gke/multitenant_fleet/fixture/main.tf new file mode 100644 index 00000000..a57db5d9 --- /dev/null +++ b/tests/blueprints/gke/multitenant_fleet/fixture/main.tf @@ -0,0 +1,54 @@ +/** + * 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. + */ + +module "test" { + source = "../../../../../blueprints/gke/multitenant-fleet" + project_id = "test-prj" + billing_account_id = "ABCDEF-0123456-ABCDEF" + folder_id = "folders/1234567890" + prefix = "test" + vpc_config = { + host_project_id = "my-host-project-id" + vpc_self_link = "projects/my-host-project-id/global/networks/my-network" + } + clusters = { + mycluster = { + cluster_autoscaling = null + description = "My cluster" + dns_domain = null + location = "europe-west1" + labels = {} + net = { + master_range = "172.17.16.0/28" + pods = "pods" + services = "services" + subnet = "projects/my-host-project-id/regions/europe-west1/subnetworks/mycluster-subnet" + } + overrides = null + } + } + nodepools = { + mycluster = { + mynodepool = { + initial_node_count = 1 + node_count = 1 + node_type = "n2-standard-4" + overrides = null + spot = false + } + } + } +} diff --git a/tests/blueprints/gke/multitenant_fleet/test_plan.py b/tests/blueprints/gke/multitenant_fleet/test_plan.py new file mode 100644 index 00000000..faafa5aa --- /dev/null +++ b/tests/blueprints/gke/multitenant_fleet/test_plan.py @@ -0,0 +1,20 @@ +# 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. + + +def test_resources(e2e_plan_runner): + "Test that plan works and the numbers of resources is as expected." + modules, resources = e2e_plan_runner() + assert len(modules) == 4 + assert len(resources) == 24 From d6482c7b38fbe68fd17c670cf26755500c37885f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Mon, 12 Sep 2022 11:59:30 +0200 Subject: [PATCH 16/16] addmissing boilerplate --- tests/blueprints/gke/__init__.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/blueprints/gke/__init__.py b/tests/blueprints/gke/__init__.py index e69de29b..6d6d1266 100644 --- a/tests/blueprints/gke/__init__.py +++ b/tests/blueprints/gke/__init__.py @@ -0,0 +1,13 @@ +# 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.