From 8a7dda94b2821aade7deadae9d417a3fda95405f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 1 Feb 2022 13:19:07 +0100 Subject: [PATCH 01/10] Update README.md --- fast/stages/00-bootstrap/README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fast/stages/00-bootstrap/README.md b/fast/stages/00-bootstrap/README.md index 52e6ef271..64c306626 100644 --- a/fast/stages/00-bootstrap/README.md +++ b/fast/stages/00-bootstrap/README.md @@ -190,7 +190,9 @@ Below is the outline of the output files generated by this stage: ### Running the stage -The first `apply` run as a user needs a special runtime variable, so that the user roles are preserved when setting IAM bindings: +Before running `init` and `apply`, check your environment so no extra variables that might influence authentication are present (e.g. `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT`). In general, you should use user application credentials, and FAST will then take care to provision autmoation identities and configure impersonation for you. + +When running the first `apply` as a user, you need to pass a special runtime variable so that the user roles are preserved when setting IAM bindings. ```bash terraform init From c727abfa3b971197b83d606ba53fc5b7e547216f Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 1 Feb 2022 13:20:19 +0100 Subject: [PATCH 02/10] Update README.md --- fast/stages/00-bootstrap/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fast/stages/00-bootstrap/README.md b/fast/stages/00-bootstrap/README.md index 64c306626..cf91f7725 100644 --- a/fast/stages/00-bootstrap/README.md +++ b/fast/stages/00-bootstrap/README.md @@ -190,7 +190,7 @@ Below is the outline of the output files generated by this stage: ### Running the stage -Before running `init` and `apply`, check your environment so no extra variables that might influence authentication are present (e.g. `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT`). In general, you should use user application credentials, and FAST will then take care to provision autmoation identities and configure impersonation for you. +Before running `init` and `apply`, check your environment so no extra variables that might influence authentication are present (e.g. `GOOGLE_IMPERSONATE_SERVICE_ACCOUNT`). In general you should use user application credentials, and FAST will then take care to provision automation identities and configure impersonation for you. When running the first `apply` as a user, you need to pass a special runtime variable so that the user roles are preserved when setting IAM bindings. From 6e896382d6112eafbbdf479d0c4212a551fbad9f Mon Sep 17 00:00:00 2001 From: lcaggio Date: Tue, 1 Feb 2022 18:12:57 +0100 Subject: [PATCH 03/10] Fix READMEs. (#484) * Fix READMEs. * fix outputs location paths in READMEs * fix output location paths in READMEs * Update README.md * Update README.md Co-authored-by: Ludovico Magnocavallo --- fast/stages/00-bootstrap/README.md | 8 +- fast/stages/01-resman/README.md | 10 ++- fast/stages/02-networking/README.md | 12 +-- fast/stages/02-security/README.md | 12 +-- fast/stages/03-project-factory/prod/README.md | 74 +++++++++---------- 5 files changed, 61 insertions(+), 55 deletions(-) diff --git a/fast/stages/00-bootstrap/README.md b/fast/stages/00-bootstrap/README.md index cf91f7725..c9b0fa714 100644 --- a/fast/stages/00-bootstrap/README.md +++ b/fast/stages/00-bootstrap/README.md @@ -162,7 +162,7 @@ At any time during the life of this stage, you can configure it to automatically Automatic generation of files is disabled by default. To enable the mechanism, set the `outputs_location` variable to a valid path on a local filesystem, e.g. ```hcl -outputs_location = "../../configs" +outputs_location = "../../config" ``` Once the variable is set, `apply` will generate and manage providers and variables files, including the initial one used for this stage after the first run. You can then link these files in the relevant stages, instead of manually transfering outputs from one stage, to Terraform variables in another. @@ -203,13 +203,15 @@ terraform apply \ Once the initial `apply` completes successfully, configure a remote backend using the new GCS bucket, and impersonation on the automation service account for this stage. To do this, you can use the generated `providers.tf` file if you have configured output files as described above, or extract its contents from Terraform's output, then migrate state with `terraform init`: ```bash -# if using output files via the outputs_location variable -ln -s [path set in outputs_location]/00-bootstrap/* ./ +# if using output files via the outputs_location and set to `../../config` +ln -s ../../config/00-bootstrap/* ./ # or from outputs if not using output files terraform output -json providers | jq -r '.["00-bootstrap"]' \ > providers.tf # migrate state to GCS bucket configured in providers file terraform init -migrate-state +# run terraform apply to remo user iam binding +terraform apply ``` ## Customizations diff --git a/fast/stages/01-resman/README.md b/fast/stages/01-resman/README.md index 098c65062..50f8edaf5 100644 --- a/fast/stages/01-resman/README.md +++ b/fast/stages/01-resman/README.md @@ -53,8 +53,8 @@ To simplify setup, the previous stage pre-configures a valid providers file in i If you have set a valid value for `outputs_location` in the bootstrap stage, simply link the relevant `providers.tf` file from this stage's folder in the path you specified: ```bash -# `outputs_location` is set to `../../configs/example` -ln -s ../../configs/example/01-resman/providers.tf +# `outputs_location` is set to `../../config` +ln -s ../../config/01-resman/providers.tf ``` If you have not configured `outputs_location` in bootstrap, you can derive the providers file from that stage's outputs: @@ -65,6 +65,8 @@ terraform output -json providers | jq -r '.["01-resman"]' \ > ../01-resman/providers.tf ``` +If you want to continue to rely on `outputs_location` logic, create a `terraform.tfvars` file and configure it as deacribed [here](../00-bootstrap/#output-files-and-cross-stage-variables). + ### Variable configuration There are two broad sets of variables you will need to fill in: @@ -77,8 +79,8 @@ To avoid the tedious job of filling in the first group of variable with values d If you configured a valid path for `outputs_location` in the bootstrap stage, simply link the relevant `terraform-*.auto.tfvars.json` files from this stage's outputs folder (under the path you specified), where the `*` above is set to the name of the stage that produced it. For this stage, a single `.tfvars` file is avalaible: ```bash -# `outputs_location` is set to `../../configs/example` -ln -s ../../configs/example/01-resman/terraform-bootstrap.auto.tfvars.json +# `outputs_location` is set to `../../config` +ln -s ../../config/01-resman/terraform-bootstrap.auto.tfvars.json ``` A second set of variables is specific to this stage, they are all optional so if you need to customize them, create an extra `terraform.tfvars` file. diff --git a/fast/stages/02-networking/README.md b/fast/stages/02-networking/README.md index 3f9854758..f655a1740 100644 --- a/fast/stages/02-networking/README.md +++ b/fast/stages/02-networking/README.md @@ -136,8 +136,8 @@ To simplify setup, the previous stage pre-configures a valid providers file in i If you have set a valid value for `outputs_location` in the bootstrap stage, simply link the relevant `providers.tf` file from this stage's folder in the path you specified: ```bash -# `outputs_location` is set to `../../configs/example` -ln -s ../../configs/example/02-networking/providers.tf +# `outputs_location` is set to `../../config` +ln -s ../../config/02-networking/providers.tf ``` If you have not configured `outputs_location` in bootstrap, you can derive the providers file from that stage's outputs: @@ -160,11 +160,13 @@ To avoid the tedious job of filling in the first group of variables with values If you have set a valid value for `outputs_location` in the bootstrap and in the resman stage, simply link the relevant `terraform-*.auto.tfvars.json` files from this stage's folder in the path you specified, where the `*` above is set to the name of the stage that produced it. For this stage, a single `.tfvars` file is available: ```bash -# `outputs_location` is set to `../../configs/example` -ln -s ../../configs/example/02-networking/terraform-bootstrap.auto.tfvars.json -ln -s ../../configs/example/02-networking/terraform-resman.auto.tfvars.json +# `outputs_location` is set to `../../config` +ln -s ../../config/02-networking/terraform-bootstrap.auto.tfvars.json +ln -s ../../config/02-networking/terraform-resman.auto.tfvars.json ``` +If you want to continue to rely on `outputs_location` logic, create a `terraform.tfvars` file and configure it as deacribed [here](../00-bootstrap/#output-files-and-cross-stage-variables). + Please refer to the [Variables](#variables) table below for a map of the variable origins, and to the sections below on how to adapt this stage to your networking configuration. ### VPCs diff --git a/fast/stages/02-security/README.md b/fast/stages/02-security/README.md index 36797f2f1..621db3e98 100644 --- a/fast/stages/02-security/README.md +++ b/fast/stages/02-security/README.md @@ -57,8 +57,8 @@ To simplify setup, the previous stage pre-configures a valid providers file in i If you have set a valid value for `outputs_location` in the resource management stage, simply link the relevant `providers.tf` file from this stage's folder in the path you specified: ```bash -# `outputs_location` is set to `../../configs/example` -ln -s ../../configs/example/02-security/providers.tf +# `outputs_location` is set to `../../config` +ln -s ../../config/02-security/providers.tf ``` If you have not configured `outputs_location` in resource management, you can derive the providers file from that stage's outputs: @@ -69,6 +69,8 @@ terraform output -json providers | jq -r '.["02-security"]' \ > ../02-security/providers.tf ``` +If you want to continue to rely on `outputs_location` logic, create a `terraform.tfvars` file and configure it as deacribed [here](../00-bootstrap/#output-files-and-cross-stage-variables). + ### Variable configuration There are two broad sets of variables you will need to fill in: @@ -81,9 +83,9 @@ To avoid the tedious job of filling in the first group of variables with values If you configured a valid path for `outputs_location` in the previous stages, simply link the relevant `terraform-*.auto.tfvars.json` files from this stage's output folder (under the path you specified), where the `*` above is set to the name of the stage that produced it. For this stage, two `.tfvars` files are available: ```bash -# `outputs_location` is set to `../../configs/example` -ln -s ../../configs/example/02-security/terraform-bootstrap.auto.tfvars.json -ln -s ../../configs/example/02-security/terraform-resman.auto.tfvars.json +# `outputs_location` is set to `../../config` +ln -s ../../config/02-security/terraform-bootstrap.auto.tfvars.json +ln -s ../../config/02-security/terraform-resman.auto.tfvars.json ``` A second set of optional variables is specific to this stage. If you need to customize them, create an extra `terraform.tfvars` file. diff --git a/fast/stages/03-project-factory/prod/README.md b/fast/stages/03-project-factory/prod/README.md index 34f9be88a..70b6cfce6 100644 --- a/fast/stages/03-project-factory/prod/README.md +++ b/fast/stages/03-project-factory/prod/README.md @@ -15,51 +15,50 @@ Projects for each environment across different teams are created by dedicated se The project factory takes care of the following activities: -* Project creation -* API/Services enablement -* Service accounts creation -* IAM roles assignment for groups and service accounts -* KMS keys roles assignment -* Shared VPC attachment and subnets IAM binding -* DNS zones creation and visibility configuration -* Project-level org policies definition -* Billing setup (billing account attachment and budget configuration) -* Essential contacts definition (for [budget alerts](https://cloud.google.com/billing/docs/how-to/budgets) and [important notifications](https://cloud.google.com/resource-manager/docs/managing-notification-contacts?hl=en)) +- Project creation +- API/Services enablement +- Service accounts creation +- IAM roles assignment for groups and service accounts +- KMS keys roles assignment +- Shared VPC attachment and subnets IAM binding +- DNS zones creation and visibility configuration +- Project-level org policies definition +- Billing setup (billing account attachment and budget configuration) +- Essential contacts definition (for [budget alerts](https://cloud.google.com/billing/docs/how-to/budgets) and [important notifications](https://cloud.google.com/resource-manager/docs/managing-notification-contacts?hl=en)) - ## 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`](../../02-networking) and [`02-security`](../../02-security)) have been run. It's of course possible to run this stage in isolation, by making sure the architectural prerequisites are satisfied (e.g., networking), and that the Service Account running the stage is granted the roles/permissions below: -* One service account per environment, each with appropriate permissions - * at the organization level a custom role for networking operations including the following permissions - * `"compute.organizations.enableXpnResource"`, - * `"compute.organizations.disableXpnResource"`, - * `"compute.subnetworks.setIamPolicy"`, - * `"dns.networks.bindPrivateDNSZone"` - * and role `"roles/orgpolicy.policyAdmin"` - * on each folder where projects are created - * `"roles/logging.admin"` - * `"roles/owner"` - * `"roles/resourcemanager.folderAdmin"` - * `"roles/resourcemanager.projectCreator"` - * on the host project for the Shared VPC - * `"roles/browser"` - * `"roles/compute.viewer"` - * `"roles/dns.admin"` -* If networking is used (e.g., for VMs, GKE Clusters or AppEngine flex), VPC Host projects and their subnets should exist when creating projects -* If per-environment DNS sub-zones are required, one "root" zone per environment should exist when creating projects (e.g., prod.gcp.example.com.) +- One service account per environment, each with appropriate permissions + - at the organization level a custom role for networking operations including the following permissions + - `"compute.organizations.enableXpnResource"`, + - `"compute.organizations.disableXpnResource"`, + - `"compute.subnetworks.setIamPolicy"`, + - `"dns.networks.bindPrivateDNSZone"` + - and role `"roles/orgpolicy.policyAdmin"` + - on each folder where projects are created + - `"roles/logging.admin"` + - `"roles/owner"` + - `"roles/resourcemanager.folderAdmin"` + - `"roles/resourcemanager.projectCreator"` + - on the host project for the Shared VPC + - `"roles/browser"` + - `"roles/compute.viewer"` + - `"roles/dns.admin"` +- If networking is used (e.g., for VMs, GKE Clusters or AppEngine flex), VPC Host projects and their subnets should exist when creating projects +- If per-environment DNS sub-zones are required, one "root" zone per environment should exist when creating projects (e.g., prod.gcp.example.com.) ### Providers configuration If you're running this on top of Fast, you should run the following commands to create the providers file, and populate the required variables from the previous stage. ```bash -# Variable `outputs_location` is set to `../../configs/example` in stage 01-resman +# Variable `outputs_location` is set to `../../config` in stage 01-resman $ cd fabric-fast/stages/03-project-factory/prod -ln -s ../../../configs/example/03-project-factory-prod/providers.tf +ln -s ../../../config/03-project-factory-prod/providers.tf ``` ### Variable configuration @@ -74,18 +73,17 @@ To avoid the tedious job of filling in the first group of variables with values If you configured a valid path for `outputs_location` in the bootstrap and networking stage, simply link the relevant `terraform-*.auto.tfvars.json` files from this stage's outputs folder (under the path you specified), where the `*` above is set to the name of the stage that produced it. For this stage, a single `.tfvars` file is available: ```bash -# Variable `outputs_location` is set to `../../configs/example` in stages 01-bootstrap and 02-networking -ln -s ../../../configs/example/03-project-factory-prod/terraform-bootstrap.auto.tfvars.json -ln -s ../../../configs/example/03-project-factory-prod/terraform-networking.auto.tfvars.json +# Variable `outputs_location` is set to `../../config` in stages 01-bootstrap and 02-networking +ln -s ../../../config/03-project-factory-prod/terraform-bootstrap.auto.tfvars.json +ln -s ../../../config/03-project-factory-prod/terraform-networking.auto.tfvars.json ``` If you're not using Fast, refer to the [Variables](#variables) table at the bottom of this document for a full list of variables, their origin (e.g., a stage or specific to this one), and descriptions explaining their meaning. -Besides the values above, a project factory takes 2 additional inputs: +Besides the values above, a project factory takes 2 additional inputs: -* `data/defaults.yaml`, manually configured by adapting the [`prod/data/defaults.yaml.sample`](./prod/data/defaults.yaml.sample), which defines per-environment default values e.g., for billing alerts and labels. - -* `data/projects/*.yaml`, one file per project (optionally grouped in folders), which configures each project. A [`prod/data/projects/project.yaml.sample`](./prod/data/projects/project.yaml.sample) is provided as reference and documentation for the schema. Projects will be named after the filename, e.g., `fast-prod-lab0.yaml` will create project `fast-prod-lab0`. +- `data/defaults.yaml`, manually configured by adapting the [`prod/data/defaults.yaml.sample`](./prod/data/defaults.yaml.sample), which defines per-environment default values e.g., for billing alerts and labels. +- `data/projects/*.yaml`, one file per project (optionally grouped in folders), which configures each project. A [`prod/data/projects/project.yaml.sample`](./prod/data/projects/project.yaml.sample) is provided as reference and documentation for the schema. Projects will be named after the filename, e.g., `fast-prod-lab0.yaml` will create project `fast-prod-lab0`. Once the configuration is complete, run the project factory by running From b0d32af600dc49660e25c2c4932193e22c2f7828 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 1 Feb 2022 18:32:47 +0100 Subject: [PATCH 04/10] Experimental module to derive DNS inbound policy addresses (#482) * first version * add README --- .../net-dns-policy-address/README.md | 35 +++++++++++++++++++ .../net-dns-policy-address/main.tf | 35 +++++++++++++++++++ .../net-dns-policy-address/outputs.tf | 31 ++++++++++++++++ .../net-dns-policy-address/variables.tf | 27 ++++++++++++++ 4 files changed, 128 insertions(+) create mode 100644 modules/__experimental/net-dns-policy-address/README.md create mode 100644 modules/__experimental/net-dns-policy-address/main.tf create mode 100644 modules/__experimental/net-dns-policy-address/outputs.tf create mode 100644 modules/__experimental/net-dns-policy-address/variables.tf diff --git a/modules/__experimental/net-dns-policy-address/README.md b/modules/__experimental/net-dns-policy-address/README.md new file mode 100644 index 000000000..bf345a9b5 --- /dev/null +++ b/modules/__experimental/net-dns-policy-address/README.md @@ -0,0 +1,35 @@ +# Google Cloud DNS Inbound Policy Addresses + +This module allows discovering the addresses reserved in subnets when [DNS Inbound Policies](https://cloud.google.com/dns/docs/policies) are configured. + +Since it's currently impossible to fetch those addresses using a GCP data source (see [this issue](https://github.com/hashicorp/terraform-provider-google/issues/3753) for more details), the workaround used here is to derive the authorization token from the Google provider, and do a direct HTTP call to the Compute API. + +## Examples + +```hcl +module "dns-policy-addresses" { + source = "./modules/_experimental/net-dns-policy-addresses" + project_id = "myproject" + regions = ["europe-west1", "europe-west3"] +} +# tftest skip +``` + +The output is a map with lists of addresses of type `DNS_RESOLVER` for each region specified in variables. + + + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [project_id](variables.tf#L17) | Project id. | string | ✓ | | +| [regions](variables.tf#L22) | Regions to fetch addresses from. | list(string) | | ["europe-west1"] | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [addresses](outputs.tf#L24) | DNS inbound policy addresses per region. | | + + diff --git a/modules/__experimental/net-dns-policy-address/main.tf b/modules/__experimental/net-dns-policy-address/main.tf new file mode 100644 index 000000000..1a5079efa --- /dev/null +++ b/modules/__experimental/net-dns-policy-address/main.tf @@ -0,0 +1,35 @@ +/** + * 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 { + url = format( + "https://content-compute.googleapis.com/compute/v1/projects/%s", + var.project_id + ) +} + +data "google_client_config" "current" { +} + +data "http" "addresses" { + for_each = toset(var.regions) + url = "${local.url}/regions/${each.key}/addresses?filter=purpose%20%3D%20%22DNS_RESOLVER%22" + + # Optional request headers + request_headers = { + Authorization = "Bearer ${data.google_client_config.current.access_token}" + } +} diff --git a/modules/__experimental/net-dns-policy-address/outputs.tf b/modules/__experimental/net-dns-policy-address/outputs.tf new file mode 100644 index 000000000..d379f2683 --- /dev/null +++ b/modules/__experimental/net-dns-policy-address/outputs.tf @@ -0,0 +1,31 @@ +/** + * 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 { + region_addresses = { + for k, v in data.http.addresses : k => try(jsondecode(v.body), {}) + } +} + + +output "addresses" { + description = "DNS inbound policy addresses per region." + value = { + for k, v in local.region_addresses : k => [ + for i in try(v.items, []) : i.address + ] + } +} diff --git a/modules/__experimental/net-dns-policy-address/variables.tf b/modules/__experimental/net-dns-policy-address/variables.tf new file mode 100644 index 000000000..1b80d160f --- /dev/null +++ b/modules/__experimental/net-dns-policy-address/variables.tf @@ -0,0 +1,27 @@ +/** + * 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 "project_id" { + description = "Project id." + type = string +} + +variable "regions" { + description = "Regions to fetch addresses from." + nullable = false + type = list(string) + default = ["europe-west1"] +} From 9c9f13a81d59449a3c31c8ebdb20536741c67807 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 1 Feb 2022 19:00:47 +0100 Subject: [PATCH 05/10] Update README.md --- modules/__experimental/net-dns-policy-address/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/__experimental/net-dns-policy-address/README.md b/modules/__experimental/net-dns-policy-address/README.md index bf345a9b5..4c61e216c 100644 --- a/modules/__experimental/net-dns-policy-address/README.md +++ b/modules/__experimental/net-dns-policy-address/README.md @@ -8,7 +8,7 @@ Since it's currently impossible to fetch those addresses using a GCP data source ```hcl module "dns-policy-addresses" { - source = "./modules/_experimental/net-dns-policy-addresses" + source = "./modules/__experimental/net-dns-policy-addresses" project_id = "myproject" regions = ["europe-west1", "europe-west3"] } From c6310173a4a4a154f84d1ca6718cd8a72796b711 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 1 Feb 2022 19:01:08 +0100 Subject: [PATCH 06/10] Update README.md --- modules/__experimental/net-dns-policy-address/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/__experimental/net-dns-policy-address/README.md b/modules/__experimental/net-dns-policy-address/README.md index 4c61e216c..36f9e4e1e 100644 --- a/modules/__experimental/net-dns-policy-address/README.md +++ b/modules/__experimental/net-dns-policy-address/README.md @@ -8,9 +8,9 @@ Since it's currently impossible to fetch those addresses using a GCP data source ```hcl module "dns-policy-addresses" { - source = "./modules/__experimental/net-dns-policy-addresses" - project_id = "myproject" - regions = ["europe-west1", "europe-west3"] + source = "./modules/__experimental/net-dns-policy-addresses" + project_id = "myproject" + regions = ["europe-west1", "europe-west3"] } # tftest skip ``` From ac36d588bb201eb8ab65229dbfe8e7aad4179341 Mon Sep 17 00:00:00 2001 From: Ludovico Magnocavallo Date: Tue, 1 Feb 2022 19:02:15 +0100 Subject: [PATCH 07/10] Update main.tf --- modules/__experimental/net-dns-policy-address/main.tf | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/modules/__experimental/net-dns-policy-address/main.tf b/modules/__experimental/net-dns-policy-address/main.tf index 1a5079efa..cb3144de7 100644 --- a/modules/__experimental/net-dns-policy-address/main.tf +++ b/modules/__experimental/net-dns-policy-address/main.tf @@ -21,14 +21,11 @@ locals { ) } -data "google_client_config" "current" { -} +data "google_client_config" "current" {} data "http" "addresses" { for_each = toset(var.regions) url = "${local.url}/regions/${each.key}/addresses?filter=purpose%20%3D%20%22DNS_RESOLVER%22" - - # Optional request headers request_headers = { Authorization = "Bearer ${data.google_client_config.current.access_token}" } From 98b238ae7afad125afbb59ec79e008e108601417 Mon Sep 17 00:00:00 2001 From: apichick Date: Wed, 2 Feb 2022 07:59:21 +0100 Subject: [PATCH 08/10] =?UTF-8?q?Updated=20modules=20README=20to=20include?= =?UTF-8?q?=20details=20around=20module=20versioning=20an=E2=80=A6=20(#476?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Updated modules README to include details around module versioning and how to best use the modules * Update README.md Co-authored-by: apichick Co-authored-by: Ludovico Magnocavallo Co-authored-by: Ludovico Magnocavallo --- modules/README.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/modules/README.md b/modules/README.md index 31770ec5c..9b1913c74 100644 --- a/modules/README.md +++ b/modules/README.md @@ -8,6 +8,25 @@ Authoritative IAM bindings are primarily used (e.g. `google_storage_bucket_iam_b Specific modules also offer support for non-authoritative bindings (e.g. `google_storage_bucket_iam_member` for service accounts), to allow granular permission management on resources that they don't manage directly. +These modules are not necessarily backward compatible. Changes breaking compatibility in modules are marked by major releases (but not all major releases contain breaking changes). Please be mindful when upgrading Fabric modules in existing Terraform setups, and always try to use versioned references in module sources so you can easily revert back to a previous version. Since the introduction of the `moved` block in Terraform we try to use it whenever possible to make updates non-breaking, but that does not cover all changes we might need to make. + +These modules are used in the examples included in this repository. If you are using any of those examples in your own Terraform configuration, make sure that you are using the same version for all the modules, and switch module sources to GitHub format using references. The recommended approach to working with Fabric modules is the following: + +- Fork the repository and own the fork. This will allow you to: + - Evolve the existing modules. + - Create your own modules. + - Sync from the upstream repository to get all the updates. +- Use GitHub sources with refs to reference the modules in your fork. See an example below: + + ``` + module "project" { + source = "github.com/my-fork/cloud-foundation-fabric.git//modules/project?ref=v12.0.0" + name = "my-project" + billing_account = "123456-123456-123456" + parent = "organizations/123456" + } + ``` + ## Foundational modules - [billing budget](./billing-budget) From 5396735bc64f543fa552a0d7cf570c9009d8caf0 Mon Sep 17 00:00:00 2001 From: Julio Castillo Date: Wed, 2 Feb 2022 08:32:59 +0100 Subject: [PATCH 09/10] Changes to gcs to bq least privilege example (#447) * Changes to gcs to bq least privilege example * Fix 'try' on encryption variables * Fix roles * Fix tests * Use templatefile in output variables * Remove FIXME * Fix tests * Changes to gcs to bq least privilege example * Fix 'try' on encryption variables * Fix roles * Fix tests * Use templatefile in output variables * Remove FIXME * Fix tests * Merge branch 'jccb/gcs-to-bq-changes' of https://github.com/GoogleCloudPlatform/cloud-foundation-fabric into jccb/gcs-to-bq-changes * fix readme and template * fix readme * Update FIXME. Co-authored-by: Lorenzo Caggioni Co-authored-by: Ludovico Magnocavallo --- .../gcs-to-bq-with-least-privileges/README.md | 29 ++++++----- .../bigquery.tftpl | 2 + .../dataflow.tftpl | 18 +++++++ .../datastorage.tf | 24 ++++----- .../gcs-to-bq-with-least-privileges/kms.tf | 2 +- .../gcs-to-bq-with-least-privileges/main.tf | 16 +++--- .../outputs.tf | 52 ++++++++++--------- .../serviceaccounts.tf | 4 -- .../terraform.tfvars.sample | 4 +- .../gcs-to-bq-with-least-privileges/vpc.tf | 4 -- .../test_plan.py | 2 +- 11 files changed, 81 insertions(+), 76 deletions(-) create mode 100644 examples/data-solutions/gcs-to-bq-with-least-privileges/bigquery.tftpl create mode 100644 examples/data-solutions/gcs-to-bq-with-least-privileges/dataflow.tftpl diff --git a/examples/data-solutions/gcs-to-bq-with-least-privileges/README.md b/examples/data-solutions/gcs-to-bq-with-least-privileges/README.md index 078ba2647..8804fcc95 100644 --- a/examples/data-solutions/gcs-to-bq-with-least-privileges/README.md +++ b/examples/data-solutions/gcs-to-bq-with-least-privileges/README.md @@ -3,17 +3,18 @@ This example creates the infrastructure needed to run a [Cloud Dataflow](https://cloud.google.com/dataflow) pipeline to import data from [GCS](https://cloud.google.com/storage) to [Bigquery](https://cloud.google.com/bigquery). The example will create different service accounts with least privileges on resources. To run the pipeline, users listed in `data_eng_principals` can impersonate all those service accounts. The solution will use: - - internal IPs for GCE and Cloud Dataflow instances - - Cloud NAT to let resources egress to the Internet, to run system updates and install packages - - rely on [Service Account Impersonation](https://cloud.google.com/iam/docs/impersonating-service-accounts) to avoid the use of service account keys - - Service Accounts with least privilege on each resource - - (Optional) CMEK encription for GCS bucket, DataFlow instances and BigQuery tables - -The example is designed to match real-world use cases with a minimum amount of resources and some compromise listed below. It can be used as a starting point for more complex scenarios. +- internal IPs for GCE and Cloud Dataflow instances +- Cloud NAT to let resources egress to the Internet, to run system updates and install packages +- rely on [Service Account Impersonation](https://cloud.google.com/iam/docs/impersonating-service-accounts) to avoid the use of service account keys +- Service Accounts with least privilege on each resource +- (Optional) CMEK encription for GCS bucket, DataFlow instances and BigQuery tables + +The example is designed to match real-world use cases with a minimum amount of resources and some compromises listed below. It can be used as a starting point for more complex scenarios. This is the high level diagram: ![GCS to Biquery High-level diagram](diagram.png "GCS to Biquery High-level diagram") + ## Move to real use case consideration In the example we implemented some compromise to keep the example minimal and easy to read. On a real word use case, you may evaluate the option to: - Configure a Shared-VPC @@ -93,13 +94,13 @@ We need to create 3 file: - A `person_udf.js` containing the UDF javascript file used by the Dataflow template. - A `person_schema.json` file containing the table schema used to import the CSV. -You can find an example of those file in the folder `./data-demo`. You can copy the example files in the GCS bucket using the command returned in the terraform output as `command-01-gcs`. Below an example: +You can find an example of those file in the folder `./data-demo`. You can copy the example files in the GCS bucket using the command returned in the terraform output as `command_01_gcs`. Below an example: ```bash gsutil -i gcs-landing@PROJECT.iam.gserviceaccount.com cp data-demo/* gs://LANDING_BUCKET ``` -We can now run the Dataflow pipeline using the `gcloud` returned in the terraform output as `command-02-dataflow`. Below an example: +We can now run the Dataflow pipeline using the `gcloud` returned in the terraform output as `command_02_dataflow`. Below an example: ```bash gcloud --impersonate-service-account=orch-test@PROJECT.iam.gserviceaccount.com dataflow jobs run test_batch_01 \ @@ -119,7 +120,7 @@ outputTable=PROJECT:datalake.person,\ bigQueryLoadingTemporaryDirectory=gs://PREFIX-df-tmp ``` -You can check data imported into Google BigQuery using the command returned in the terraform output as `command-03-bq`. Below an example: +You can check data imported into Google BigQuery using the command returned in the terraform output as `command_03_bq`. Below an example: ``` bq query --use_legacy_sql=false 'SELECT * FROM `PROJECT.datalake.person` LIMIT 1000' @@ -144,10 +145,10 @@ bq query --use_legacy_sql=false 'SELECT * FROM `PROJECT.datalake.person` LIMIT 1 |---|---|:---:| | [bq_tables](outputs.tf#L15) | Bigquery Tables. | | | [buckets](outputs.tf#L20) | GCS bucket Cloud KMS crypto keys. | | -| [command-01-gcs](outputs.tf#L43) | gcloud command to copy data into the created bucket impersonating the service account. | | -| [command-02-dataflow](outputs.tf#L48) | Command to run Dataflow template impersonating the service account. | | -| [command-03-bq](outputs.tf#L70) | BigQuery command to query imported data. | | +| [command_01_gcs](outputs.tf#L43) | gcloud command to copy data into the created bucket impersonating the service account. | | +| [command_02_dataflow](outputs.tf#L48) | Command to run Dataflow template impersonating the service account. | | +| [command_03_bq](outputs.tf#L69) | BigQuery command to query imported data. | | | [project_id](outputs.tf#L28) | Project id. | | -| [serviceaccount](outputs.tf#L33) | Service account. | | +| [service_accounts](outputs.tf#L33) | Service account. | | diff --git a/examples/data-solutions/gcs-to-bq-with-least-privileges/bigquery.tftpl b/examples/data-solutions/gcs-to-bq-with-least-privileges/bigquery.tftpl new file mode 100644 index 000000000..3be2492e2 --- /dev/null +++ b/examples/data-solutions/gcs-to-bq-with-least-privileges/bigquery.tftpl @@ -0,0 +1,2 @@ +bq query --project_id=${project_id} --use_legacy_sql=false \ + 'SELECT * FROM `${project_id}.${bigquery_dataset}.${bigquery_table}` LIMIT ${sql_limit}' \ No newline at end of file diff --git a/examples/data-solutions/gcs-to-bq-with-least-privileges/dataflow.tftpl b/examples/data-solutions/gcs-to-bq-with-least-privileges/dataflow.tftpl new file mode 100644 index 000000000..820770fbe --- /dev/null +++ b/examples/data-solutions/gcs-to-bq-with-least-privileges/dataflow.tftpl @@ -0,0 +1,18 @@ +gcloud \ + --impersonate-service-account=${sa_orch_email} \ + dataflow jobs run test_batch_01 \ + --gcs-location gs://dataflow-templates/latest/GCS_Text_to_BigQuery \ + --project ${project_id} \ + --region ${region} \ + --disable-public-ips \ + --subnetwork ${subnet} \ + --staging-location ${gcs_df_stg} \ + --service-account-email ${sa_df_email} \ + %{ if cmek_encryption }--dataflow-kms-key=${kms_key_df} %{ endif } \ + --parameters \ +javascriptTextTransformFunctionName=transform,\ +JSONPath=${data_schema_file},\ +javascriptTextTransformGcsPath=${data_udf_file},\ +inputFilePattern=${data_file},\ +outputTable=${project_id}:${bigquery_dataset}.${bigquery_table},\ +bigQueryLoadingTemporaryDirectory=${gcs_df_tmp} \ No newline at end of file diff --git a/examples/data-solutions/gcs-to-bq-with-least-privileges/datastorage.tf b/examples/data-solutions/gcs-to-bq-with-least-privileges/datastorage.tf index 357111255..98b15bb20 100644 --- a/examples/data-solutions/gcs-to-bq-with-least-privileges/datastorage.tf +++ b/examples/data-solutions/gcs-to-bq-with-least-privileges/datastorage.tf @@ -12,10 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -############################################################################### -# GCS # -############################################################################### - module "gcs-data" { source = "../../../modules/gcs" project_id = module.project.project_id @@ -23,7 +19,7 @@ module "gcs-data" { name = "data" location = var.region storage_class = "REGIONAL" - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.cmek_encryption ? module.kms[0].keys.key-gcs.id : null force_destroy = true } @@ -34,22 +30,20 @@ module "gcs-df-tmp" { name = "df-tmp" location = var.region storage_class = "REGIONAL" - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-gcs.id, null) : null + encryption_key = var.cmek_encryption ? module.kms[0].keys.key-gcs.id : null force_destroy = true } -############################################################################### -# BQ # -############################################################################### - module "bigquery-dataset" { source = "../../../modules/bigquery-dataset" project_id = module.project.project_id id = "datalake" location = var.region - # Define Tables in Terraform for the porpuse of the example. - # Probably in a production environment you would handle Tables creation in a - # separate Terraform State or using a different tool/pipeline (for example: Dataform). + + # Note: we define tables in Terraform for the purpose of this + # example. A production environment would probably handle table + # creation in a separate terraform pipeline or using a different + # tool (for example: Dataform) tables = { person = { friendly_name = "Person. Dataflow import." @@ -64,10 +58,10 @@ module "bigquery-dataset" { deletion_protection = false options = { clustering = null - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null + encryption_key = var.cmek_encryption ? module.kms[0].keys.key-bq.id : null expiration_time = null } } } - encryption_key = var.cmek_encryption ? try(module.kms[0].keys.key-bq.id, null) : null + encryption_key = var.cmek_encryption ? module.kms[0].keys.key-bq.id : null } diff --git a/examples/data-solutions/gcs-to-bq-with-least-privileges/kms.tf b/examples/data-solutions/gcs-to-bq-with-least-privileges/kms.tf index d7133542b..5e6166308 100644 --- a/examples/data-solutions/gcs-to-bq-with-least-privileges/kms.tf +++ b/examples/data-solutions/gcs-to-bq-with-least-privileges/kms.tf @@ -17,7 +17,7 @@ module "kms" { source = "../../../modules/kms" project_id = module.project.project_id keyring = { - name = "${var.prefix}-keyring", + name = "${var.prefix}-keyring" location = var.region } keys = { diff --git a/examples/data-solutions/gcs-to-bq-with-least-privileges/main.tf b/examples/data-solutions/gcs-to-bq-with-least-privileges/main.tf index e2a50e4fc..8c8486440 100644 --- a/examples/data-solutions/gcs-to-bq-with-least-privileges/main.tf +++ b/examples/data-solutions/gcs-to-bq-with-least-privileges/main.tf @@ -58,19 +58,18 @@ locals { module.service-account-orch.iam_email, ] "roles/iam.serviceAccountTokenCreator" = concat( - var.data_eng_principals, - ) - "roles/viewer" = concat( var.data_eng_principals ) # Dataflow roles - "roles/dataflow.admin" = concat([ - module.service-account-orch.iam_email, - ], var.data_eng_principals + "roles/dataflow.admin" = concat( + [module.service-account-orch.iam_email], + var.data_eng_principals ) "roles/dataflow.worker" = [ module.service-account-df.iam_email, ] + "roles/dataflow.developer" = var.data_eng_principals + "roles/compute.viewer" = var.data_eng_principals # network roles "roles/compute.networkUser" = [ module.service-account-df.iam_email, @@ -79,10 +78,6 @@ locals { } } -############################################################################### -# Projects # -############################################################################### - module "project" { source = "../../../modules/project" name = var.project_id @@ -101,6 +96,7 @@ module "project" { "storage.googleapis.com", "storage-component.googleapis.com", ] + # additive IAM bindings avoid disrupting bindings in existing project iam = var.project_create != null ? local.iam : {} iam_additive = var.project_create == null ? local.iam : {} diff --git a/examples/data-solutions/gcs-to-bq-with-least-privileges/outputs.tf b/examples/data-solutions/gcs-to-bq-with-least-privileges/outputs.tf index 2114d2f90..7ffd06340 100644 --- a/examples/data-solutions/gcs-to-bq-with-least-privileges/outputs.tf +++ b/examples/data-solutions/gcs-to-bq-with-least-privileges/outputs.tf @@ -30,7 +30,7 @@ output "project_id" { value = module.project.project_id } -output "serviceaccount" { +output "service_accounts" { description = "Service account." value = { bq = module.service-account-bq.email @@ -40,36 +40,38 @@ output "serviceaccount" { } } -output "command-01-gcs" { +output "command_01_gcs" { description = "gcloud command to copy data into the created bucket impersonating the service account." value = "gsutil -i ${module.service-account-landing.email} cp data-demo/* ${module.gcs-data.url}" } -output "command-02-dataflow" { +output "command_02_dataflow" { description = "Command to run Dataflow template impersonating the service account." - value = < Date: Wed, 2 Feb 2022 15:21:10 +0100 Subject: [PATCH 10/10] M4CE (v5) Examples (#413) * M4CE (v5) Examples * vm-migration new parent folder * New vm-migration section * Updated variables description * Updated variables description * Fixed broken link * Updated variables description * Fix lines spacing * Added output variable * Updated Variables description * New variables layout * fixed new line * M4CE (v5) Examples * vm-migration new parent folder * New vm-migration section * Updated variables description * Updated variables description * Fixed broken link * Updated variables description * Fix lines spacing * Added output variable * Updated Variables description * New variables layout * fixed new line * added test * move test on new folder * Updated variables order and description * Added output file * vm-migration example tests * Updated output description * Updated output description * Fixed Typo Co-authored-by: Simone Ruffilli Co-authored-by: Ludovico Magnocavallo --- examples/cloud-operations/README.md | 7 +- .../cloud-operations/vm-migration/README.md | 34 +++++++ .../vm-migration/esxi/README.md | 43 +++++++++ .../vm-migration/esxi/backend.tf.sample | 20 ++++ .../vm-migration/esxi/diagram.png | Bin 0 -> 16388 bytes .../vm-migration/esxi/main.tf | 58 +++++++++++ .../vm-migration/esxi/provider.tf | 20 ++++ .../vm-migration/esxi/variables.tf | 66 +++++++++++++ .../vm-migration/esxi/vsphere.tf | 37 +++++++ .../host-target-projects/README.md | 40 ++++++++ .../host-target-projects/backend.tf.sample | 20 ++++ .../host-target-projects/diagram.png | Bin 0 -> 34623 bytes .../vm-migration/host-target-projects/main.tf | 73 ++++++++++++++ .../host-target-projects/outputs.tf | 18 ++++ .../host-target-projects/variables.tf | 44 +++++++++ .../host-target-sharedvpc/README.md | 44 +++++++++ .../host-target-sharedvpc/backend.tf.sample | 20 ++++ .../host-target-sharedvpc/diagram.png | Bin 0 -> 34273 bytes .../host-target-sharedvpc/main.tf | 84 ++++++++++++++++ .../host-target-sharedvpc/outputs.tf | 18 ++++ .../host-target-sharedvpc/variables.tf | 48 ++++++++++ .../vm-migration/single-project/README.md | 41 ++++++++ .../single-project/backend.tf.sample | 20 ++++ .../vm-migration/single-project/diagram.png | Bin 0 -> 26745 bytes .../vm-migration/single-project/main.tf | 90 ++++++++++++++++++ .../vm-migration/single-project/outputs.tf | 18 ++++ .../vm-migration/single-project/variables.tf | 51 ++++++++++ .../host_target_projects/__init__.py | 13 +++ .../host_target_projects/fixture/main.tf | 43 +++++++++ .../host_target_projects/test_plan.py | 26 +++++ .../host_target_sharedvpc/__init__.py | 13 +++ .../host_target_sharedvpc/fixture/main.tf | 51 ++++++++++ .../host_target_sharedvpc/test_plan.py | 26 +++++ .../vm_migration/single_project/__init__.py | 13 +++ .../single_project/fixture/main.tf | 31 ++++++ .../vm_migration/single_project/test_plan.py | 25 +++++ 36 files changed, 1154 insertions(+), 1 deletion(-) create mode 100644 examples/cloud-operations/vm-migration/README.md create mode 100644 examples/cloud-operations/vm-migration/esxi/README.md create mode 100644 examples/cloud-operations/vm-migration/esxi/backend.tf.sample create mode 100644 examples/cloud-operations/vm-migration/esxi/diagram.png create mode 100644 examples/cloud-operations/vm-migration/esxi/main.tf create mode 100644 examples/cloud-operations/vm-migration/esxi/provider.tf create mode 100644 examples/cloud-operations/vm-migration/esxi/variables.tf create mode 100644 examples/cloud-operations/vm-migration/esxi/vsphere.tf create mode 100644 examples/cloud-operations/vm-migration/host-target-projects/README.md create mode 100644 examples/cloud-operations/vm-migration/host-target-projects/backend.tf.sample create mode 100644 examples/cloud-operations/vm-migration/host-target-projects/diagram.png create mode 100644 examples/cloud-operations/vm-migration/host-target-projects/main.tf create mode 100644 examples/cloud-operations/vm-migration/host-target-projects/outputs.tf create mode 100644 examples/cloud-operations/vm-migration/host-target-projects/variables.tf create mode 100644 examples/cloud-operations/vm-migration/host-target-sharedvpc/README.md create mode 100644 examples/cloud-operations/vm-migration/host-target-sharedvpc/backend.tf.sample create mode 100644 examples/cloud-operations/vm-migration/host-target-sharedvpc/diagram.png create mode 100644 examples/cloud-operations/vm-migration/host-target-sharedvpc/main.tf create mode 100644 examples/cloud-operations/vm-migration/host-target-sharedvpc/outputs.tf create mode 100644 examples/cloud-operations/vm-migration/host-target-sharedvpc/variables.tf create mode 100644 examples/cloud-operations/vm-migration/single-project/README.md create mode 100644 examples/cloud-operations/vm-migration/single-project/backend.tf.sample create mode 100644 examples/cloud-operations/vm-migration/single-project/diagram.png create mode 100644 examples/cloud-operations/vm-migration/single-project/main.tf create mode 100644 examples/cloud-operations/vm-migration/single-project/outputs.tf create mode 100644 examples/cloud-operations/vm-migration/single-project/variables.tf create mode 100644 tests/examples/cloud_operations/vm_migration/host_target_projects/__init__.py create mode 100644 tests/examples/cloud_operations/vm_migration/host_target_projects/fixture/main.tf create mode 100644 tests/examples/cloud_operations/vm_migration/host_target_projects/test_plan.py create mode 100644 tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/__init__.py create mode 100644 tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/fixture/main.tf create mode 100644 tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/test_plan.py create mode 100644 tests/examples/cloud_operations/vm_migration/single_project/__init__.py create mode 100644 tests/examples/cloud_operations/vm_migration/single_project/fixture/main.tf create mode 100644 tests/examples/cloud_operations/vm_migration/single_project/test_plan.py diff --git a/examples/cloud-operations/README.md b/examples/cloud-operations/README.md index 1a491e94f..62f86db9c 100644 --- a/examples/cloud-operations/README.md +++ b/examples/cloud-operations/README.md @@ -50,5 +50,10 @@ The example's feed tracks changes to Google Compute instances, and the Cloud Fun This [example](./onprem-sa-key-management) shows how to manage IAM Service Account Keys by manually generating a key pair and uploading the public part of the key to GCP. -s + +
+ +## Migrate for Compute Engine (v5) + This set of [examples](./vm-migration) shows how to deploy Migrate for Compute Engine (v5) on top of existing Cloud Foundations on different scenarios. An example on how to deploy the M4CE connector on VMWare ESXi is also part of the examples. +
\ No newline at end of file diff --git a/examples/cloud-operations/vm-migration/README.md b/examples/cloud-operations/vm-migration/README.md new file mode 100644 index 000000000..0c75af091 --- /dev/null +++ b/examples/cloud-operations/vm-migration/README.md @@ -0,0 +1,34 @@ +# Migrate for Compute Engine (v5) examples + +The examples in this folder implement **Migrate for Compute Engine (v5)** environments for the main migration scenarios like the ones with host and target project, or with shared VPC. + +They are meant to be used as minimal but complete starting points to create migration environment **on top of existing cloud foundations**, and as playgrounds to experiment with specific Google Cloud features. + +## Examples + +### M4CE on a single project + + This [example](./single-project/) implements a simple environment for Migrate for Compute Engine (v5) where both the API backend and the migration target environment are deployed on a single GCP project. + +This example represents the easiest sceario to implement a Migrate for Compute Engine (v5) enviroment suitable for small migrations on simple enviroments or for product showcases. +
+ +### M4CE with host and target projects + + This [example](./host-target-projects/) implements a Migrate for Compute Engine (v5) host and target projects topology where the API backend and access grants are implemented on the host project while workloads are migrated on a different target project. + +This example shows a complex scenario where Migrate for Compute Engine (v5) can be deployed on top of and existing HUB and SPOKE topology and the migration target projects are deployed with platform foundations. +
+ +### M4CE with Host and Target Projects and Shared VPC + + This [example](./host-target-sharedvpc/) implements a Migrate for Compute Engine (v5) host and target projects topology as described above with the support of shared VPC. + +The example shows how to implement a Migrate for Compute Engine (v5) environment on top of an existing shared VPC topology where the shared VPC service projects are the target projects for the migration. +
+ +### ESXi Connector + + This [example](./esxi/) implements a Migrate for Compute Engine (v5) environment on a VMWare ESXi cluster as source for VM migrations. + +The example shows how to deploy the Migrate for Compute Engine (v5) connector and implement all the security prerequisites for the migration to GCP. \ No newline at end of file diff --git a/examples/cloud-operations/vm-migration/esxi/README.md b/examples/cloud-operations/vm-migration/esxi/README.md new file mode 100644 index 000000000..8e77f37db --- /dev/null +++ b/examples/cloud-operations/vm-migration/esxi/README.md @@ -0,0 +1,43 @@ +# M4CE(v5) - ESXi Connector + +This example deploys a virtual machine from an OVA image and the security prerequisites to run the Migrate for Compute Engine (v5) [connector](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/migrate-connector) on VMWare ESXi. + +The example is designed to deploy the M4CE (v5) connector on and existing VMWare environment. The [network configuration](https://cloud.google.com/migrate/compute-engine/docs/5.0/concepts/architecture#migration_architecture) required to allow the communication of the migrate connetor to the GCP API is not included in this example. + +This is the high level diagram: + +![High-level diagram](diagram.png "High-level diagram") + +## Managed resources and services + +This sample creates several distinct groups of resources: + +- virtual machine + - [M4CE migrate connector](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/migrate-connector#installing_the_migrate_connector) +- IAM + - [vCenter user role](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/migrate-connector#step-1) + + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [m4ce_ssh_public_key](variables.tf#L43) | Filesystem path to the public key for the SSH login | string | ✓ | | +| [vcenter_password](variables.tf#L48) | VCenter user password. | string | ✓ | | +| [vsphere_environment](variables.tf#L53) | VMVware VSphere connection parameters | object({…}) | ✓ | | +| [m4ce_appliance_properties](variables.tf#L15) | M4CE connector OVA image configuration parameters | object({…}) | | {…} | +| [m4ce_connector_ovf_url](variables.tf#L37) | http URL to the public M4CE connector OVA image | string | | "https://storage.googleapis.com/vmmigration-public-artifacts/migrate-connector-2-0-1663.ova" | + + +## Manual Steps +Once this example is deployed a VCenter user has to be created and binded to the M4CE role in order to allow the connector access the VMWare resources. +The user can be created manually through the VCenter web interface or througt GOV commandline if it is available: +```bash +export GOVC_URL= (eg. https://192.168.1.100/sdk) +export GOVC_USERNAME= (eg. administrator@example.local) +export GOVC_PASSWORD= +export GOVC_INSECURE=true + +govc sso.user.create -p -R gcp-m4ce-role gcp-m4ce-user +govc permissions.set -principal gcp-m4ce-user@example.local -propagate=true -role gcp-m4ce-role +``` diff --git a/examples/cloud-operations/vm-migration/esxi/backend.tf.sample b/examples/cloud-operations/vm-migration/esxi/backend.tf.sample new file mode 100644 index 000000000..99f84b17c --- /dev/null +++ b/examples/cloud-operations/vm-migration/esxi/backend.tf.sample @@ -0,0 +1,20 @@ +# Copyright 2021 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" { + bucket = "" + } +} diff --git a/examples/cloud-operations/vm-migration/esxi/diagram.png b/examples/cloud-operations/vm-migration/esxi/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..45ccb361020058a8da9d3dec8a601a2252e2cc23 GIT binary patch literal 16388 zcmdVBWmH_7$RnB9s-SF;GFM&z?QQka;Je`s~?rug5=RIN->c z$*|3{XCi|#5~Av!hI?5ajwCbgIcKrvso!kBGd<)neE<38yFQDK`+)gON;Wg3@o4YF z*<)#L-LK<&eLNoy#mkQ*@7B>@Z)LiS@S$_C^#{vi6XC=?e_%_shhlIj z_tr6_H5kWTv^+S^MOoX`z(<6pJAIS|>V9#|T;?$6!|Tb+#WB+JyE+%*=)lT_i%qh> zM=*I|MTC{HK}A0!i51+rAn2QEAXJf>I@;CWkQ7<@D=g-10Xl^U5 z`xwPEwyzW2wDIqPjimV&_qAIr5kvlf4iUCem-Wi`@}n?)Uz>B%yk-LbTa+$s{dQ-J z@9bQ!M|yi#W(yd_1bX#bU3-q&T&&dLZrwII-#8ISiD_%kX}7+tl;Qna6#HP3w)f&{A74{E80Y5_g;19Zg}Y(;niB)VOTMe8qR= zrA&5}RPL>rpOi&L#te6N zxgh(j`IW%>-CHYl3lm8h14d_TYFQo0WZoCla60QrQ|Lp*m0D`Cot>Q^*+`io&*k+< zLxCHlLsCT7=#ih-y|=-ghx`<1;wd4S$YR~<@s$IG9>dEs>qBt71SR^0I(CLSq^iUN zI<2CQUdfvT>8-KuyhCD9l5kvk6`6W(T`(5Kg*4Dp;IcIe!NExsD7+x&HEMHIaqRmY zmAbdPpQq`3au{aRt=qvSAkgw^woRvTDw-#NNY8m>hG!Lgrx039KjXu(U0HQ85N7!Fa}8=|7L#xG>#o*(9?=SMIHE%R_R55&@YNBc zSHnU2;)gvZ2JqDqw1p@=nEsVo+!qxrOngjkOe#~MU#SFXUg`vo+nIIHjIiv3%bHiM zcF9mNO=#`ZY)?eR~&L-OZvje4&3d}*!teqWc8gg7l_Z!QqdS~yV6w^eG zP^R9F4wS$6X9zzdY0n$I7`f?#%e>U}wqL0dBfnvwOeTdhILivw$}ybk_&k~UdQz4U zYnpI@!-nXyF_qK&WfD_9bMYr*q^UvZ^Xj*72s)PtVKO$1f+4uN*L`z{wDDyW!-0dP zrM8p}RoC3|j19fBvu%ZCO1FzRvfO4=YYTGYLS3tTI?FR}ED20MWP~f3U7#VhI6WCo zDPTAU?IDu1#uz!%Q@qgq#o^XGt8%*opXGH1WH=cNRFXk9L=k*@>p6dvRw%6)KD^9}oM*NWSV6r(mPK;r0|DXsSeMMsWSKAX?b`&Sl_>nccOn6P~oHE#yLsVK*Pb(Xz6Qs zd%6P7gtwq8v9sPLnJ13;(`Zq+$*b{RW0e$(=rQ;Li9mZmQbPeBUo%K_;zTx{e%Xjp zsj{-N+-j%WI07=^A{*kR{OOD2div|!?{3DgBqL#9v)T95{Pd&)gMhcZ`t(JW^7|7&%8>9__gM1+vB&#w$A7FuYwtb$NpPyLxMQ{}pVEj3CRGdp!cebLl zqT-v(ZhFgclgaq7=x3yq4!-3fz^|x0=7{kxO5>7DItkjuA9^2?=PI&(@HR;n3VM{( zPe(%^$*u#FcV1@Uu|^cl{my$6c&Am6Ug>R6Pp7xR)-&hG3?6&%ptQ;#37UM<8R>57 zXlv#Vv#4ziZYSIvtb(WK7~Mp#5zOk*Rvk4px#^CAI={;8=mG&Ah}_6J2ZS6|3A37< zoM@TtFtDc$Tj8Y`R$csyTv83{SD9nQmjsBI={m}bC2@<`j-Q``I~P=RHBL*D@!gB| z0io7A1gB*!ZxanpVy_UWXg@6h>4AVa-BHPmzQA>=?7ZM|evT~!mm4)!$E zsG>cnLW$Qw9yy75WMqH4)%oa^ZwM!DY3k?fD10Euzj3o$RkyOTNAP!QIH4NOI3Aogl76z6ZlBdmL^$f z@fzA4_WJ(I>ojd;g=Uv8bz$ZUho8HLI)Ant^S_r5%vG61YmM)n^}#gyo6f#xo|pg0 zmlMZjH|UgsiGflWEWpj)B|4sdN12MT$>lyz%@4s9B>~1?N9G=K2`BdYV38d_8T{QIqw}x+L}Znj z0Xa)?XkdnvPu<*w;5Kw}_a2@A{q6CYnEC0a(XEsz?<>I&d*#~P)nJ^;=Gt06b$!(H zR{=J+@17Fb%Vxx=#)1IByf1<*d}t_=kcWo%=C7r#c2%e?@n{OSG`KyY+L;jNik-%i z<5Kj-C$Hw~e>0Ve3vG+FDNl{D$QZI!wN%0n#Q2(u&X2&+Dyz<=tLgp{sHNs35|rpw zS6}#)W|lCqmcS98x-zCr+#JhS)SwG(b%`bY+E7<}uwj%ApYbJVu-xk12ut^`zUsG% z;WBJpxImp9b{*lf}gUKc4NBU{DR@H7RTeBqJlEQwgClY?^lf+JTi8 zM!}VScILKH^sfr-V`re=E9`A z7nc=;B*m|dSW!hqw3|aCBPs5V$c>w!#RT|C$NL4hr9H+=2m;l7YjQ62EJI7pwaZt= zd|<;F{cY z0&-#ht#%pH&Afom&DVFPuEn~GWhy24%{a8^3ym~_f+U7N+JR{dLbfEb@W{#Ao0=GE z-oJm3j)76Rx3_1vF7fNvFIX>Hd}`{}!9h-THdM7_OjTJK`vEspjB?0PJ`R#CUZ5Jw zfS(s+*?whPZr;s)_2Kvh=>(gadFBzIw&`%r>8Goy!g`7)dkf%jHWR1AaE z)zy)Zkc4EwGcqu^_k*Roi5sMZc$zaa*l+&EA`}hz9(r&Z`SW_5g?m+JoLqd`v^UDU zyCYB|Z#B61<50@-rq!YOGCuJqy8)P;F>42J8R<)F9w!Y;$9nb|)>iqg=gLm(R=IIk z_dF=tK*+h^*pq-w@k>?H)KmvOCB4S%1^;1U1e5&YoR-#-wyNfH$*IXH54Ve<;n8YC z?b6yZKMM<~rmYoNmY1{a)C51LOWd?>)S`Z{*0+KJ$=ht)6mJNG5u|eL>h5l8=IN=u zjbLVG#;p5-ezy7XSVRjcIJwB5Qx(;k0}h+@%c=y7}Io)qFZv&`+UZ+3anv*jiJpiq5=Y zjtLWJTw25%o^xmDo?iSmiFm6dyX8Zvy`?bBAx*1Aw;59s3gG>B+SHxHzqX)_zmtij z;>Vo^2L~q>VsM!PGh2cVMh(u7u#(POU%h8vTf^}}ki5uRh~8i1Y=Xoz_)X8>x1m*4 zk5@WndYQXLHx@8EbNv|!hYyE-gSJ8}=pojL=dGBnjfhbXLus5M4_Bi{)&08b&U}cz z*MExoISyIt+Ha5Lj*wpZU#^7L*Vx+HR;1iD&@eEdj)>i>YG`Qa>FKGfznFfj{qp6@ z3iK+n_w1J_tpN{h6$H&9amVRYD?b2RQO18W^l)lKP=43E;S*3;a|Ph2fD*;#isNEm zb1yN%0?m@JzSMcnRAFaT&gqteVvYzkg@b#;01R!m1tI5fB&di%!_NAuih-H{4^Pqf z{X}+F#&>85TGS_~gVlU_{bCZ23&tSRSF<%%gJz7z?p|6v{8W6r)HgIm{QQju&0e(a z6;{^PC0{Boi=w7?h%@qU$T3@7dFSgz3?+o$?D>#-yI>e!V51SJ#R)IxHa82@r9qx? zkWLA3aCGWo?tso&@HjvX@I*K+(PFt)6UT=uUAw!sE!b^sZGhsSdDsBJT$h>F(Cf!UD8OU4IX`nT{n^f)utsZfZ{AOjG!8eIK#c z9A$0oeF;PlCzJv0b@!N&;$-SpZN(@#xjstJKxVn)Fk_5xdqrGYme0yDMtis@e6YBf zfTE9;_DeD%csa6g2#o${Cp{$OMNLCvV`Vs#-Jk7k*mx6S%ftQ6$jC@30gV`0i2T-D zXj*2bL~=;<_4W18ki4zBmKNGQoR^nZ1tGVxQe`B{xyb>4Xjs_Sd(#8)%D;Vms0UD; ziIz(e%vDfY^06%h8b4|?dKYXL#v9+q<2k`D`fgW)!$d za+-B9W@gQ)2-K~-IML_6-B1+>>%ObU!HB+^UaX7qr%zP|%Q5%&&og5^Uy*!mZr=JM zJ`B5*s4lHVv|M5$CLv*RZlf!!d#_W~1T}R?6U=Lg7_*qz6d!6s#)akc?3T@N`ugYJ-+98@LVVWBrJhJzp>g-~;598ildW^Bz zC~%CqQqcSEjR*+#;mC%g+hFz5g=$3;2$WHwJH%`kjyJmaOjlpO6BJC-&Cbu?yT}}b zYrA_umGJ|v&H6nEn`CNsv15^kx3j}W-#u<`R-^X4)qMjMjbWBwEUc{c9+pG{aAR(<7_@tC6qbZ4cw2CTLOCa` z-l%FCdhM^)o_<<*e{25HAUKU&Bzs_(^+Wcj{dD4t`DQblm)N+dCAkn2lkvemqfG6U z5URYj(!Kq)8Ixy2gs;flZbtUsP-Z5i9*CbFZ-E@ob~Ly!F)<4Z3QXYn5Fp_Y;p=Ux zbqcG}($d}i{az_=?`xhVUl&~ivmCZ7&J#&|vMa`21lOPA$kB$ON}vNvA;>A?qFo3? z&_yex_cERK0;kbjgg8zAVZ8}m(I&eTdat}8aF2IN zN=gYhS>PkSL0a8~FG14$Qk{{?`_*(UUjP03jm&o6wXpbz@8R_aT@(-jX~FLO<3-`2 z4L4C{a9-kEECJN?%v468*~_rmZHv5JBzA_s zxd{OXjx&u274WkQt+%)E;_?SGZ~3kN=&b7b#~Jp#p*CpouCCqFAn@yfcDNggzASWh zb?(8TXJSD?iE440>%MOr?Cz$f;bO0EvNWgNjk+H18ayb9(xS|2Y~*YIKq07;i6u*$ znVa|>DVsDE8YnzQh=*rxYx@Z!S3MB2=yzejyOF3QTv<_ZczW6`F6c!qT=W~vKL}Zd z+YGJgr9F3&_bJS0nn{$JOR|h^EK~BAElrpdGc&nKlsdVzjCRmtt-M;QHHa+HU6|$n z0GW!LME**Dljt_n_dQRAF_uwNT5Q@p2RePXw-cw%Wo)nV3s!VKpcSxjw%a+ZB+9_M z9r^=v120*7Py2Q^ferq5ho5D=fJF*ZT-Xn3{HgOj(yU?~=pvWY1bchM2d<*}a`K4| zSxj^kaw1Z3sbC2zHeTBcC`qlisWSry4ViP`C*^O{MHWA3FtPMizJ2kqaOdV@UJF0b z>V_VjqXAA&m^L3QQISoqpakFm35lLn>VtGWQ{5HnT3+ISCzY5$%P(q3NtwgjFEpJA_jgzX%X!C<3{1@LpJQVXH@1}8TG>u%nz$F|v*GEH5OPZe%sYl9EEc->G~2W`u%MhpKKrS!_%<8N(=^x-T#!G%_X zg^39zB_H06DPk_cx8eSun-ky<=3EIZo4A?!mX@@CnORw-Z~Djse3hr=6&0l?5j=t zuH;5SeJde9mdDuePeKQIfE-^5%U=Ku1L1AF5=Hy_WpcKW@~5-Sm5uNI*NfQP-rn8; z6SLpypwH2;yvAy_`S-VByb0;wc@FX|EvRzeMovyL;ds;1&{p>MK#va3zn7GSLx6=d zu`r3|LM=*7N}x%-khMoP)HUpVEiD@I3&$ukg`%Mlv^5R8{T2GX(FMp;8bq$v@INr`d1orKNtl3SW+L_~6C-uwFMTjA18|vNJo9qu3C|6T2^y)Pd9lAU+trGW_A{R=jP$*A#a%-_wSXx zJ^X#RpPuUUY?`a>WC%Q*Q%Xl$KxH_xT18W&!`!ebhozx^CTGCOBVwej4>vvEW~QmC z`&{9pP!I>wAd1{LZ1U^ZZ%y9{&%;kr*usS$A|fSyC8Wid>~&Ws;%1G9D+@hi_%55Xsj0L3 zdE=bR@7W9plPyWyb2nEvIr)(A;-2ekq__mO>B-689&Ur922J?*M9k>D)Lp1OJ>To+ zTYXnRFzhX=7C28Q3fYwD{K5nL4;WubWJ}a(s0$8vKp)L)Y$E-hsXoIR@1MXHw6xNm zXF>Ow$%F$6t=%&FXERt>SmNyQx-Bg&unkH9f#&V)?Qkq|0AlQ0wU04!b8|B>p+^hM z3b@|h-Nh4$GJ3)m7WV%BjameN2NViL!XkTtgk-ikJFAP1hK@coIM`M3`7@!2NPB51 z(~7(yTBAkloyRD2dh#||_hm~1S7oujt|ns+TC%rJffxSI1v&02(P)i{>c8Nn%Mv+$ z!yOPCw=fwAC{Pei^_{$8k7_s^Hta2jbKfMK8>;{JJo_DT5{WX`tJ%HFZd_bx?E;Kw zg&0NX7OPQggk1N}07AkbjcER2=|uo=q6UYANt;CQH+svy{H zsikEfEsZj<_tj;y*X435qYFkA$Tm-cgQF3N*_8Sln><-++>7S`VqQgA^l%m@g3V21 z4H}aTMXFbdL4&sy-4BV)xZhw`D9XH_>fHI#~`i{8eP9{8q=p$9Uwe&b@L0HUfOKq!3OIDTp$17EFHz`gOUG0r_mT z=%T#vQ5{R#zP7G$8V&Rs|x)isF3S zy|^fVpmlz3_RFW(v?8C9>=xI})pen|s_$AN=-Ha~Ebp!7ePnW(nV&e0bLjXuJAp^} zNvcP|=AS)>fTfMwg1?7R-QZ&VxVO39okN7dUh+d_>_v&+xALt27;w+dT{$bmudKrd zbxm+oE%z-~Ae$xnyemA+CsR-o@gcYIY6MUMXNC?ezrZq=v{}E=UoT^F2ndl>Tb=ZL z!j6NCbU%kiYTZ0PXa%=o)z;LIydoRmpg6TK-{J>iH^)1u*K-901*UP>_>|hasO68n zy;4)1!l}#a>)m8k7#$$Cm(DCeDfUn#FE39KZxT#21dfY3Oe;A(b#2tgoc<`+dG{8o zlg6S~{*Xf~8PK45sD6XR-H3g*cHH)>FiHYC=I@TK&dm_}P?HLjsgvbjKJG_1 z?Paa-?rg1=!An=#6J(3`(Fz=z$-9dq0$e1xuS_hgqx_gl_ndeMpYMp0scRUZRacA} zu4?JJDKJ-kYPQh)yp>&R@Yb>Ta>SS&`}qVTt`VzD=uXR?AH!j@5z3fnRe^t=Ra)?9{|;%xmkFKQ{`d%NVet?|LyN?<}baWVvQ#l!I~!dOkMn z%#`WiQhm(LT}i2{3k=K+>=0e2lJYo+j#k=T?BWnLn~GTI#`Z0KC!Q# zD-w}u*3{LlHNe9McZN%{V>Js%HSkzYgIrvcv35#xS5_?5tU4;%?gK4|ns5nG^j1nk z7#rP<;IH<85ic?R<4qHuvH$HGr8BklxCWD%7$kG-$8AX&gwZIbFMV&cyk+uiv$w*XXu;6`t_>O?X4y4j`phbyGx`F}J=eLJlSnRou zJfzeAFr@HR)zzb!d?zXAXcut_A0KS@1CcQ>YOAZwz5?{rOY$Hifav`rRRoeI^4DPytbk1*3*;|isiY8Sz-U@6vbk-o+-2u1f885w0`XIvmsAsqG?(u zruTf^uWfD3e3EqqJ1RI{?L0KqpQ7~@v>M$0`=HJe|1oG3Zg`Q!|LNUD8!SBi|B`0> za?$tAr_hpv-Bd*nIM{qbUFM6~ z4R{};AAV1Ri#rVugy@SCVy=Vy0E0nCx@)j7gV?<3(r1dj+7OJh$?qK9I)pouHA8IfxJ@uPB=~IZ9_Jo%gFh@enYbS*>&mhQJ zxg2ip+Mv+kNTBKa5~?p}Vpcb9XV+g{4HtpAg$4WTs|kZP7;kbRg{E$5LUr>CoLa3p zpOw!|1>KdB=E7=>)S$z1TN}XMoA7SWfY*Lzf0&Az`Yqz|?P0YxUTB-Ih-is&+UjXt zvJHL-Xsyj3O1h@Xtf_B8#49Hiy`rBaVW$V6>E3b(%_|T0x_Zqb?&O7xL4yRCDu%5C zQHqxsBRU9!X}h{|{l!2>?|xS2 zH~+ZkE1m|`ZMCXAX`o5>nY=#_UR-yI$a6bJo?Q1(52)fQNFitMyf%s;!+}E@8CP+MhoW zDbdlI? z>pPJM=JZ>fep8B%!@~-;%SH_}?Cj%xec>2~Cns{UvOy4a20V!GL z7M4y@92^`XBBHCSt9R~SXt};th{YqE{Q$?kM5M~t7t6HHNW~xUPfVa~TdlSgBmq84bohFC*xAIqOs%(6(Y_052#c)C$x^+%6d0%0!h? z9bk^Mq4{|d9rQY!SFhNf5%))|uC7)Dwjfd=On=#Qc5)I84duXa5$2nqd~&oAeNKew z_ZZ$*+$kP))Ac+WbHszArfSu-93%R`zq$glMfLM4ZQ@u%sF#Num%gj}jWdR7a#$E* zUG#3@*RQ71kd(qgnn!vWDAUZ~Q%rQ9x3;#Lo14SlVubqAQczH^vX)m=U|TD?yK@r< zH!)FDQ@?%<$A<~wLth7VI*tVco2MIJREmCsTzOAt=2DW}Xo2Tka&u)mo>Q$&cU>Q4 z9#G4jM$O;s*BFM@4-Q%Rf;3$$P|k{b`^WUih$-b>8vonW!8G|6`?M{?2C<9A-(Oy0 zwnNhnXi!@ zVzcGnrwAl)1oK*3h4}c=>}Y9d+}+({K7IPZp{LgzP>;Y2R^I1jJ z5oZKMR;rVUVX3n0eyr?>N6eG!j=OQ%Uu=dEeGcs>U88;sKb?>eDV7QkW%Em{{^8+J zj<|~T^>thVf-dlGTSh{{D_e>Ulbj!=P^eU7h{?vrMoCGDBL?EfEk5ce69|X!J20i( z(VBPBt{#*GTH#0D`xXFYLV9@Fme1z5kQBe5uk-FbmJWE?Z1D!o~6l$Eml==AWJ$b`1|{Ndh!xajgJSixK}T!0B{5yNvOfXk6-$? zlg7+(G_|^&NZDx0fvp2-b9*JkJ^SGk+|Sn@9v-#2t(BE8Nk~4e76ZZ2kIBW<6z)78 zxYKXp_isF`cp^y&i7zA>Sy`mS#8d!kAsXJi~X z`uH>hwofJ_FCP*-_Hh?+kC~ZycdmM3Vj^yG*J`l1qJoWDG%6};yYrdJNSFBI97!83 ztnGTG&kgp!j1IDFx5afe{A2c1)5bp5ZkumqhJp4|@}pQy%meBL-w!TFeiTUo!6`S? zqUd^b&6}y&lF?`DFDCABV|qnQ#$wgM^;V8AfE=H4FmT~nS?#Xqs9wRtS$K=c&C#+` z-gQhpL7;w~ z(xXehg^trcZY9O^HqK*8CTWF4+L$BsyurPR_<8+r;lg3hcypK-&*RMhZ_=p$7bLj4 zZJ?%2HQ3LZ3sXv(95`?LaD)CNLOTFm-zeXN-wWfK@Ww$-Q67VHKlu9p07}l$!cQJc z?>Y>)or%fH=4Rm-&^rYS3$qB8mX&1zPTzZe?(X8^A}b4Id<4djWA}v!8vxC^lL6YC zcM?=_sj0LJCILUdj;ONKN=iy1A|eH<1Ny&y|L$SA{wl=6g1OfZfk~Q9YR1IG2mmT6 zUhdOBAOO*n7)wwri6T%hSdz0%8^cGMT)lLOX8Z748AeD(7N)PNrsn78H#axuApyoU zHjBY~LX^Yceu2lPR*7+TQDvn{)0{ADeqOyq9roc{T*a={uaw$au*owHf(}#ITN|oV z7G~yQ5py>;4cNw`OfxJPkR0?dX$+D9j-KC~#qyN3c%~_WKw%+s!DsO3RH8Z1h{(u<}_rj%EWpk-T3a^7H3UV5?@_ft8n(>^VNJii$$3 z$_Hw8z-%H!h|k0{H8lbJP*B$QgfmY6q?7LHi2-2n)wu7)f=5?Z*W-rCM2CkX%>f3h z#*hsBu(2V34h|0X_ZR2q<$(3nzp<%NN0pTu#5e^#w*iKRG`i>F~31l0vhNoErR(B0<**qz$;Kgbqoy5p`zFDID1wHg5BzX zt$CWA?xq9qG&`G&>Fn_Ekd3+6L%FAdH~XcI@#U{=ZKY;rk`NNor49~&UmW#r_kjVSz&kD$qqf^cp92{d zbTr?ay?QKiVc)fbRPA4{?NzK_l8MosoSYnv#B7VBcOo)T`D#sdcEa7Xz%{|b$zo8S z%y}04?llks7X5GRN^}iJLeU76&)(7UKZS77BIv-#$o+CKRzAxNIp91Af*ZKNCr9W= z3yna)*#ulyHI(FqfPlcC{LH6VvCz0VTMJS3BFVhFwS!Nx-YwfF-OKWEZoIv^#{uMQ z0A$@tj}^JswuuQ=Gc;t$^1#B5K78&(t#&gbY4l{_043vdUOGvFWGUjnci=V*aPtxp z5n*Ru8LbH6EI z{C9VELV|)jJ3GtifV^XCYwYFYGsdzYuD#34Uk+)FzI>jVngWUsKpI54SD(ISqN@7p zRVL|Pt5g#7LU%sxPh)+vPJ32m=m6ty~KL_w#PDX~fVPag|he<>O zz)Z%T1F;>wR<~^2DkQw$$e8G;USpXO6dLXg$n^*@i>AQ(PYeG~5`k}&0+e(AJIOkp zLg~N7n-e4{@6!I<(fiMd33cJ|yO{qQKLGnL$@Tvw??2{VeMUwG`U04eI?vb=>uEV)DHKr-%so zt5?T=igMb{rt~aFGTAvfArMF+CO0(6WW_Ew5={25qS zfGC-?UyZd4($kv*Z1udNv2g~Vo*flbRTJv#0U;ANnUHq~MzU-u=gfGMRGFqo+VS3vwSIU-R zEg>cKS_uUeRaQw!nnmqVP`19cCI04JQryTFw1KGB*1PlbL_9r1!!~g+t|$pHzq0}W zqm841h{8)j@wUO8@z7?P%@}FG(8mPYAq4~t4gy$6qZHwfV~}lrVIe*~{#Pl8jSVUq z+T6?E%EtdGEkg~~R#%U!z`p9xw2X}JB_$ob{r&F%Ngy|nT*AV_LY}h?0)UGf=*Frr zQvM2bg6ZUXM+Tstj8{IB>-&94oF+|U1V%u+S&~NDJw;#p@5vrxN0mA0;C;a zhiBYfT~B}f_@K`c{y|#~D4|dK*ZN|KiHRSp%Np`e>FMb<_VyMR2V7iSyu7>>dp$jp z=xTJhuxy|{AtLHvd0k@SFW@P&v+32h z6*cHBRV00X&(czRQBmLMsDrO>OL1|3Umuc8Tz&n;_O_LS1FOXcKMGKdvG%)n$RJ8m zKtD_+@AU*isHT~inBLV^h66%YSr4C|x3_}CI4@sP?)kg~AW!kEuCDI!@$n%W6%~9$ zvgfm;@NeHrO6d9c8ozvb28|zhZ^g>YEV=gcCu*WDkPTI-Ym7Vd^QjvOfySfYDCLt+ z#OMdFuLffYdoD7o8m^=_HSu#J5=GV=NXjcIg#*9P(BYw>Wy%*F!a)^ztsPi0_}%fz z$-nafg^au-hLjjx5adg4ceez$t)pX{zBsnSrx<;B`}u~W7QOtIPB5J#?DJhf8h#{| zx{NJxK^%aWBB^+h!7phNa8c!}T3Y6dLZ8Ok*9WMi ztH+^+!2J&C0$*Jl;G*8VdBcV#RW%x)m-?sl z>(`(HF(Dx#6cm)=9!)K+@i)8v)c>SP(Ie;0AtC_B0^@3*+diuzwkF^D=iKvphb4bQ z*3l!eX^N7o`x8if{wd-Sfq-$~rxe*};pPUWrk02sk0$0Zyit)s34$n#SGNCz%K*^Cm)9p}XXY2j z9pJ;%_VxfE(96ikczZXw;%7b0=dJ-!T|>hU(7KvV>$iBmOu5>v9u2`y68ro2?}U&M zlD?U>HA*aOb{5zU0BhKy-6S%NVKnz3LPcZN0Q@^SISGRSS_HZ*N!q8~Z~(e8z_*BZ zyRC1<9eiaZ`ypSdf#tUK^zy27fC8+~kD4KX9E983JwXD{ae|C1{6C3 zAMrO%)u+|~bCZXU5ITUg0hR&a?|_mBASf#;D)#s7H$m^^0huS;$mAq$DfPb}Jqk7X z!OM|`j;A0c)jKhfP;>La7;%L)h{`QUhfO51BqSsRxQTS!XA#)HKf0KCbzP5Z5RS6~ z3I>e#e-r)xC7u4ez6YSkKzYiW_o)E%IMm(yS7!+LsBaOB+l2C19RJgQdHmJ^NVfmc zb@?ytng6HLtnL{8ky + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [migration_admin_users](variables.tf#L15) | List of users authorized to create a new M4CE sources and perform all other migration operations, in IAM format | list(string) | ✓ | | +| [migration_target_projects](variables.tf#L20) | List of target projects for m4ce workload migrations | list(string) | ✓ | | +| [migration_viewer_users](variables.tf#L25) | List of users authorized to retrive information about M4CE in the Google Cloud Console, in IAM format | list(string) | | [] | +| [project_create](variables.tf#L31) | Parameters for the creation of the new project to host the M4CE backend | object({…}) | | null | +| [project_name](variables.tf#L40) | Name of an existing project or of the new project assigned as M4CE host project | string | | "m4ce-host-project-000" | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [m4ce_gmanaged_service_account](outputs.tf#L15) | Google managed service account created automatically during the migrate connector registration.. It is used by M4CE to perform activities on target projects | | + + diff --git a/examples/cloud-operations/vm-migration/host-target-projects/backend.tf.sample b/examples/cloud-operations/vm-migration/host-target-projects/backend.tf.sample new file mode 100644 index 000000000..4f2bb3365 --- /dev/null +++ b/examples/cloud-operations/vm-migration/host-target-projects/backend.tf.sample @@ -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 +# +# 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" { + bucket = "" + } +} diff --git a/examples/cloud-operations/vm-migration/host-target-projects/diagram.png b/examples/cloud-operations/vm-migration/host-target-projects/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..ae0eaf34cd03b0aea216a29a3afa9404288c7643 GIT binary patch literal 34623 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 literal 0 HcmV?d00001 diff --git a/examples/cloud-operations/vm-migration/host-target-projects/main.tf b/examples/cloud-operations/vm-migration/host-target-projects/main.tf new file mode 100644 index 000000000..a0e899d5e --- /dev/null +++ b/examples/cloud-operations/vm-migration/host-target-projects/main.tf @@ -0,0 +1,73 @@ +# 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. + +module "host-project" { + source = "../../../../modules/project" + billing_account = (var.project_create != null + ? var.project_create.billing_account_id + : null + ) + name = var.project_name + parent = (var.project_create != null + ? var.project_create.parent + : null + ) + + services = [ + "cloudresourcemanager.googleapis.com", + "compute.googleapis.com", + "iam.googleapis.com", + "logging.googleapis.com", + "servicemanagement.googleapis.com", + "servicecontrol.googleapis.com", + "vmmigration.googleapis.com", + ] + + project_create = var.project_create != null + + iam_additive = { + "roles/iam.serviceAccountKeyAdmin" = var.migration_admin_users, + "roles/iam.serviceAccountCreator" = var.migration_admin_users, + "roles/vmmigration.admin" = var.migration_admin_users, + "roles/vmmigration.viewer" = var.migration_viewer_users, + } +} + +module "m4ce-service-account" { + source = "../../../../modules/iam-service-account" + project_id = module.host-project.project_id + name = "m4ce-sa" + generate_key = true +} + +module "target-projects" { + for_each = toset(var.migration_target_projects) + source = "../../../../modules/project" + name = each.key + project_create = false + + services = [ + "servicemanagement.googleapis.com", + "servicecontrol.googleapis.com", + "iam.googleapis.com", + "cloudresourcemanager.googleapis.com", + "compute.googleapis.com" + ] + + iam_additive = { + "roles/resourcemanager.projectIamAdmin" = var.migration_admin_users, + "roles/compute.viewer" = var.migration_admin_users, + "roles/iam.serviceAccountUser" = var.migration_admin_users + } +} diff --git a/examples/cloud-operations/vm-migration/host-target-projects/outputs.tf b/examples/cloud-operations/vm-migration/host-target-projects/outputs.tf new file mode 100644 index 000000000..ef78d4c7c --- /dev/null +++ b/examples/cloud-operations/vm-migration/host-target-projects/outputs.tf @@ -0,0 +1,18 @@ +# 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 "m4ce_gmanaged_service_account" { + description = "Google managed service account created automatically during the migrate connector registration.. It is used by M4CE to perform activities on target projects" + value = "serviceAccount:service-${module.host-project.number}@gcp-sa-vmmigration.iam.gserviceaccount.com" +} diff --git a/examples/cloud-operations/vm-migration/host-target-projects/variables.tf b/examples/cloud-operations/vm-migration/host-target-projects/variables.tf new file mode 100644 index 000000000..f6e3345f8 --- /dev/null +++ b/examples/cloud-operations/vm-migration/host-target-projects/variables.tf @@ -0,0 +1,44 @@ +# 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 "migration_admin_users" { + description = "List of users authorized to create a new M4CE sources and perform all other migration operations, in IAM format" + type = list(string) +} + +variable "migration_target_projects" { + description = "List of target projects for m4ce workload migrations" + type = list(string) +} + +variable "migration_viewer_users" { + description = "List of users authorized to retrive information about M4CE in the Google Cloud Console, in IAM format" + type = list(string) + default = [] +} + +variable "project_create" { + description = "Parameters for the creation of the new project to host the M4CE backend" + type = object({ + billing_account_id = string + parent = string + }) + default = null +} + +variable "project_name" { + description = "Name of an existing project or of the new project assigned as M4CE host project" + type = string + default = "m4ce-host-project-000" +} diff --git a/examples/cloud-operations/vm-migration/host-target-sharedvpc/README.md b/examples/cloud-operations/vm-migration/host-target-sharedvpc/README.md new file mode 100644 index 000000000..bf82f2368 --- /dev/null +++ b/examples/cloud-operations/vm-migration/host-target-sharedvpc/README.md @@ -0,0 +1,44 @@ +# M4CE(v5) - Host and Target Projects with Shared VPC + +This example creates a Migrate for Compute Engine (v5) environment deployed on an host project with multiple [target projects](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/enable-services#identifying_your_host_project) and shared VPCs. + +The example is designed to implement a M4CE (v5) environment on-top of complex migration landing environment where VMs have to be migrated to multiple target projects. In this example targets are alse service projects for a shared VPC. It also includes the IAM wiring needed to make such scenarios work. + +This is the high level diagram: + +![High-level diagram](diagram.png "High-level diagram") + +## Managed resources and services + +This sample creates\update several distinct groups of resources: + +- projects + - M4CE host project with [required services](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/enable-services#enabling_required_services_on_the_host_project) deployed on a new or existing project. + - M4CE target project prerequisites deployed on existing projects. +- IAM + - Create a [service account](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/migrate-connector#step-3) used at runtime by the M4CE connector for data replication + - Grant [migration admin roles](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/enable-services#using_predefined_roles) to provided user accounts. + - Grant [migration viewer role](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/enable-services#using_predefined_roles) to provided user accounts. + - Grant [roles on shared VPC](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/target-project#configure-permissions) to migration admins + + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [migration_admin_users](variables.tf#L15) | List of users authorized to create a new M4CE sources and perform all other migration operations, in IAM format | list(string) | ✓ | | +| [migration_target_projects](variables.tf#L20) | List of target projects for m4ce workload migrations | list(string) | ✓ | | +| [sharedvpc_host_projects](variables.tf#L45) | List of host projects that share a VPC with the selected target projects | list(string) | ✓ | | +| [migration_viewer_users](variables.tf#L25) | List of users authorized to retrive information about M4CE in the Google Cloud Console, in IAM format | list(string) | | [] | +| [project_create](variables.tf#L30) | Parameters for the creation of the new project to host the M4CE backend | object({…}) | | null | +| [project_name](variables.tf#L39) | Name of an existing project or of the new project assigned as M4CE host project | string | | "m4ce-host-project-000" | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [m4ce_gmanaged_service_account](outputs.tf#L15) | Google managed service account created automatically during the migrate connector registration. It is used by M4CE to perform activities on target projects | | + + +## Manual Steps +Once this example is deployed the M4CE [m4ce_gmanaged_service_account](https://cloud.google.com/migrate/compute-engine/docs/5.0/how-to/target-sa-compute-engine#configuring_the_default_service_account) has to be configured to grant the access to the shared VPC and allow the deploy of Compute Engine instances as the result of the migration. \ No newline at end of file diff --git a/examples/cloud-operations/vm-migration/host-target-sharedvpc/backend.tf.sample b/examples/cloud-operations/vm-migration/host-target-sharedvpc/backend.tf.sample new file mode 100644 index 000000000..4f2bb3365 --- /dev/null +++ b/examples/cloud-operations/vm-migration/host-target-sharedvpc/backend.tf.sample @@ -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 +# +# 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" { + bucket = "" + } +} diff --git a/examples/cloud-operations/vm-migration/host-target-sharedvpc/diagram.png b/examples/cloud-operations/vm-migration/host-target-sharedvpc/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..0746e8dede61bfffa315cdd21b930987e63dce96 GIT binary patch literal 34273 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 k@c^79{)PX?zmYiN6nzt{wbHp`0`PNJSwpE + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [migration_admin_users](variables.tf#L15) | List of users authorized to create a new M4CE sources and perform all other migration operations, in IAM format | list(string) | ✓ | | +| [migration_viewer_users](variables.tf#L20) | List of users authorized to retrive information about M4CE in the Google Cloud Console, in IAM format | list(string) | | [] | +| [project_create](variables.tf#L26) | Parameters for the creation of the new project to host the M4CE backend | object({…}) | | null | +| [project_name](variables.tf#L35) | Name of an existing project or of the new project assigned as M4CE host an target project | string | | "m4ce-host-project-000" | +| [vpc_config](variables.tf#L41) | Parameters to create a simple VPC on the M4CE project | object({…}) | | {…} | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [m4ce_gmanaged_service_account](outputs.tf#L15) | Google managed service account created automatically during the migrate connector registration. It is used by M4CE to perform activities on target projects | | + + diff --git a/examples/cloud-operations/vm-migration/single-project/backend.tf.sample b/examples/cloud-operations/vm-migration/single-project/backend.tf.sample new file mode 100644 index 000000000..4f2bb3365 --- /dev/null +++ b/examples/cloud-operations/vm-migration/single-project/backend.tf.sample @@ -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 +# +# 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" { + bucket = "" + } +} diff --git a/examples/cloud-operations/vm-migration/single-project/diagram.png b/examples/cloud-operations/vm-migration/single-project/diagram.png new file mode 100644 index 0000000000000000000000000000000000000000..b47bb2951ffc6ab68ddddb8a3578a951082681fd GIT binary patch literal 26745 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

#@Y_Kk1o-$EOX6?j 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 literal 0 HcmV?d00001 diff --git a/examples/cloud-operations/vm-migration/single-project/main.tf b/examples/cloud-operations/vm-migration/single-project/main.tf new file mode 100644 index 000000000..6adf9fbd5 --- /dev/null +++ b/examples/cloud-operations/vm-migration/single-project/main.tf @@ -0,0 +1,90 @@ +# 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. + +module "landing-project" { + source = "../../../../modules/project" + billing_account = (var.project_create != null + ? var.project_create.billing_account_id + : null + ) + name = var.project_name + parent = (var.project_create != null + ? var.project_create.parent + : null + ) + + services = [ + "cloudresourcemanager.googleapis.com", + "compute.googleapis.com", + "iam.googleapis.com", + "logging.googleapis.com", + "networkconnectivity.googleapis.com", + "servicemanagement.googleapis.com", + "servicecontrol.googleapis.com", + "vmmigration.googleapis.com" + ] + + project_create = var.project_create != null + + iam_additive = { + "roles/iam.serviceAccountKeyAdmin" = var.migration_admin_users, + "roles/iam.serviceAccountCreator" = var.migration_admin_users, + "roles/vmmigration.admin" = var.migration_admin_users, + "roles/vmmigration.viewer" = var.migration_viewer_users + } +} + +module "m4ce-service-account" { + source = "../../../../modules/iam-service-account" + project_id = module.landing-project.project_id + name = "m4ce-sa" + generate_key = true +} + +module "landing-vpc" { + source = "../../../../modules/net-vpc" + project_id = module.landing-project.project_id + name = "landing-vpc" + subnets = [ + { + ip_cidr_range = var.vpc_config.ip_cidr_range + name = "landing-vpc-${var.vpc_config.region}" + region = var.vpc_config.region + secondary_ip_range = {} + } + ] +} + +module "landing-vpc-firewall" { + source = "../../../../modules/net-vpc-firewall" + project_id = module.landing-project.project_id + network = module.landing-vpc.name + admin_ranges = [] + http_source_ranges = [] + https_source_ranges = [] + ssh_source_ranges = [] + custom_rules = { + allow-ssh = { + description = "Allow SSH from IAP" + direction = "INGRESS" + action = "allow" + sources = [] + ranges = ["35.235.240.0/20"] + targets = [] + use_service_accounts = false + rules = [{ protocol = "tcp", ports = ["22"] }] + extra_attributes = {} + } + } +} diff --git a/examples/cloud-operations/vm-migration/single-project/outputs.tf b/examples/cloud-operations/vm-migration/single-project/outputs.tf new file mode 100644 index 000000000..347eb54fc --- /dev/null +++ b/examples/cloud-operations/vm-migration/single-project/outputs.tf @@ -0,0 +1,18 @@ +# 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 "m4ce_gmanaged_service_account" { + description = "Google managed service account created automatically during the migrate connector registration. It is used by M4CE to perform activities on target projects" + value = "serviceAccount:service-${module.landing-project.number}@gcp-sa-vmmigration.iam.gserviceaccount.com" +} diff --git a/examples/cloud-operations/vm-migration/single-project/variables.tf b/examples/cloud-operations/vm-migration/single-project/variables.tf new file mode 100644 index 000000000..2d7214f47 --- /dev/null +++ b/examples/cloud-operations/vm-migration/single-project/variables.tf @@ -0,0 +1,51 @@ +# 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 "migration_admin_users" { + description = "List of users authorized to create a new M4CE sources and perform all other migration operations, in IAM format" + type = list(string) +} + +variable "migration_viewer_users" { + description = "List of users authorized to retrive information about M4CE in the Google Cloud Console, in IAM format" + type = list(string) + default = [] +} + +variable "project_create" { + description = "Parameters for the creation of the new project to host the M4CE backend" + type = object({ + billing_account_id = string + parent = string + }) + default = null +} + +variable "project_name" { + description = "Name of an existing project or of the new project assigned as M4CE host an target project" + type = string + default = "m4ce-host-project-000" +} + +variable "vpc_config" { + description = "Parameters to create a simple VPC on the M4CE project" + type = object({ + ip_cidr_range = string, + region = string + }) + default = { + ip_cidr_range = "10.200.0.0/20", + region = "us-west2" + } +} diff --git a/tests/examples/cloud_operations/vm_migration/host_target_projects/__init__.py b/tests/examples/cloud_operations/vm_migration/host_target_projects/__init__.py new file mode 100644 index 000000000..6d6d1266c --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/host_target_projects/__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/examples/cloud_operations/vm_migration/host_target_projects/fixture/main.tf b/tests/examples/cloud_operations/vm_migration/host_target_projects/fixture/main.tf new file mode 100644 index 000000000..94fb07f79 --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/host_target_projects/fixture/main.tf @@ -0,0 +1,43 @@ +# 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. + +module "host-target-projects-test" { + source = "../../../../../../examples/cloud-operations/vm-migration/host-target-projects" + project_create = var.project_create + migration_admin_users = ["user:admin@example.com"] + migration_viewer_users = ["user:viewer@example.com"] + migration_target_projects = ["${module.test-target-project.name}"] + depends_on = [ + module.test-target-project + ] +} + +variable "project_create" { + type = object({ + billing_account_id = string + parent = string + }) + default = { + billing_account_id = "1234-ABCD-1234" + parent = "folders/1234563" + } +} + +#This is a dummy project created to run this test. The example, here tested, is expected to run on top of existing foundations. +module "test-target-project" { + source = "../../../../../../modules/project" + billing_account = "1234-ABCD-1234" + name = "test-target-project" + project_create = true +} diff --git a/tests/examples/cloud_operations/vm_migration/host_target_projects/test_plan.py b/tests/examples/cloud_operations/vm_migration/host_target_projects/test_plan.py new file mode 100644 index 000000000..33f493bae --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/host_target_projects/test_plan.py @@ -0,0 +1,26 @@ +# 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. + + +import os + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_resources(e2e_plan_runner): + "Test that plan works and the numbers of resources is as expected." + modules, resources = e2e_plan_runner(FIXTURES_DIR) + assert len(modules) == 3 + assert len(resources) == 23 diff --git a/tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/__init__.py b/tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/__init__.py new file mode 100644 index 000000000..6d6d1266c --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/__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/examples/cloud_operations/vm_migration/host_target_sharedvpc/fixture/main.tf b/tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/fixture/main.tf new file mode 100644 index 000000000..dac8f5af0 --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/fixture/main.tf @@ -0,0 +1,51 @@ +# 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. + +module "host-target-sharedvpc-test" { + source = "../../../../../../examples/cloud-operations/vm-migration/host-target-sharedvpc" + project_create = var.project_create + migration_admin_users = ["user:admin@example.com"] + migration_viewer_users = ["user:viewer@example.com"] + migration_target_projects = [module.test-target-project.name] + sharedvpc_host_projects = [module.test-sharedvpc-host-project.name] + depends_on = [ + module.test-target-project, + module.test-sharedvpc-host-project, + ] +} + +variable "project_create" { + type = object({ + billing_account_id = string + parent = string + }) + default = { + billing_account_id = "1234-ABCD-1234" + parent = "folders/1234563" + } +} + +#These are a dummy projects created to run this test. The example, here tested, is expected to run on top of existing foundations. +module "test-target-project" { + source = "../../../../../../modules/project" + billing_account = "1234-ABCD-1234" + name = "test-target-project" + project_create = true +} +module "test-sharedvpc-host-project" { + source = "../../../../../../modules/project" + billing_account = "1234-ABCD-1234" + name = "test-sharedvpc-host-project" + project_create = true +} diff --git a/tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/test_plan.py b/tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/test_plan.py new file mode 100644 index 000000000..839357118 --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/host_target_sharedvpc/test_plan.py @@ -0,0 +1,26 @@ +# 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. + + +import os + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_resources(e2e_plan_runner): + "Test that plan works and the numbers of resources is as expected." + modules, resources = e2e_plan_runner(FIXTURES_DIR) + assert len(modules) == 4 + assert len(resources) == 23 diff --git a/tests/examples/cloud_operations/vm_migration/single_project/__init__.py b/tests/examples/cloud_operations/vm_migration/single_project/__init__.py new file mode 100644 index 000000000..6d6d1266c --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/single_project/__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/examples/cloud_operations/vm_migration/single_project/fixture/main.tf b/tests/examples/cloud_operations/vm_migration/single_project/fixture/main.tf new file mode 100644 index 000000000..c2750298c --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/single_project/fixture/main.tf @@ -0,0 +1,31 @@ +# 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. + +module "single-project-test" { + source = "../../../../../../examples/cloud-operations/vm-migration/single-project" + project_create = var.project_create + migration_admin_users = ["user:admin@example.com"] + migration_viewer_users = ["user:viewer@example.com"] +} + +variable "project_create" { + type = object({ + billing_account_id = string + parent = string + }) + default = { + billing_account_id = "1234-ABCD-1234" + parent = "folders/1234563" + } +} diff --git a/tests/examples/cloud_operations/vm_migration/single_project/test_plan.py b/tests/examples/cloud_operations/vm_migration/single_project/test_plan.py new file mode 100644 index 000000000..7d8a47b15 --- /dev/null +++ b/tests/examples/cloud_operations/vm_migration/single_project/test_plan.py @@ -0,0 +1,25 @@ +# 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. + +import os + + +FIXTURES_DIR = os.path.join(os.path.dirname(__file__), 'fixture') + + +def test_resources(e2e_plan_runner): + "Test that plan works and the numbers of resources is as expected." + modules, resources = e2e_plan_runner(FIXTURES_DIR) + assert len(modules) == 4 + assert len(resources) == 18